오늘의 랜덤 골프 # 6 : 롤 d20


17

시리즈 정보

우선, 이것을 다른 코드 골프 도전과 같이 취급하고 시리즈에 대해 전혀 걱정하지 않고 대답 할 수 있습니다. 그러나 모든 과제에는 리더 보드가 있습니다. 첫 번째 게시물에서 시리즈 대한 자세한 정보와 함께 리더 보드를 찾을 수 있습니다 .

시리즈에 대한 아이디어가 많이 있지만 미래의 과제는 아직 해결되지 않았습니다. 제안 사항이 있으면 관련 샌드 박스 게시물에 알려주십시오. .

홀 6 : D20 롤

탁상용 RPG에서 매우 일반적인 다이는 20 면형 다이 ( 일반적으로 d20 으로 알려진 20 면체)입니다. )입니다. 그러한 주사위를 굴리는 것이 당신의 임무입니다. 그러나 1에서 20 사이의 임의의 숫자를 반환하면 약간 사소한 것입니다. 따라서 당신의 임무는 주어진 주사위에 대한 임의의 그물을 생성하는 것입니다.

우리는 다음 그물을 사용합니다 :

여기에 이미지 설명을 입력하십시오

삼각형 스트립이므로 정수 목록으로 쉽게 표현할 수 있습니다. 예를 들어 입력이 주어진 경우 :

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

그것은 다음과 같은 주사위에 해당 할 것입니다 (재미있는 사실 : 이것은 Magic : The Gathering life counters / spin-down dice).

여기에 이미지 설명을 입력하십시오

그러나 이것이이 주사위를 나타내는 유일한 그물은 아닙니다. 얼굴을 풀는 방법에 따라 60 개의 다른 그물이 있습니다. 두 가지가 더 있습니다 :

[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]

또는 그래픽으로 (간단하게하기 위해 얼굴 레이블을 회전시키지 않았습니다) :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

도전

다이를 나타내는 정수 (위에서 설명 된)와 정수의리스트가 주어지면 주어진 다이에 해당하는 독립적으로 균일하게 임의의 d20 네트가 N출력 N됩니다. (즉, 가능한 60 개의 네트 각각은 생성 될 확률이 동일해야합니다.)

물론 PRNG의 기술적 한계로 인해 완벽한 균일 성은 불가능합니다. 제출물의 균일 성을 평가하기 위해 다음 작업은 완전히 균일 한 분포를 산출하는 것으로 간주됩니다.

  • PRNG (모든 범위에서)에서 숫자를 얻습니다.이 숫자는 (대략) 균일 한 것으로 기록되어 있습니다.
  • 모듈로나 곱셈 (또는 값을 균등하게 분배하는 다른 연산)을 통해 더 큰 세트의 숫자에 대한 균일 한 분포를 더 작은 세트에 매핑합니다. 더 큰 세트는 더 작은 세트보다 가능한 많은 값을 1024 배 이상 포함해야합니다.

이러한 가정이 주어지면 알고리즘이 완벽하게 균일 한 분포를 산출해야합니다.

프로그램은 1 초 이내에 100 개의 네트를 생성 할 수 있어야합니다 (따라서 위의 다이에 해당 할 때까지 임의의 네트를 생성하지 마십시오).

STDIN (또는 가장 가까운 대안), 명령 행 인수 또는 함수 인수를 통해 입력을 받고 STDOUT (또는 가장 가까운 대안), 함수 리턴 값 또는 함수 (out) 매개 변수를 통해 결과를 출력하는 프로그램 또는 함수를 작성할 수 있습니다.

입력 및 출력은 편리하고 모호하지 않은 플랫 목록 형식 일 수 있습니다. d20의면 값은 고유 한 양의 정수이며 언어의 자연 정수 유형에 적합하다고 가정 할 수 있습니다.

이것은 코드 골프이므로 가장 짧은 제출 (바이트)이 이깁니다. 물론 사용자 당 가장 짧은 제출물도 시리즈의 전체 순위표에 들어갑니다.

샘플 출력

입력

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

60 개의 가능한 그물 (실수하지 않은 경우)은 특별한 순서가 없습니다 :

[11, 10, 9, 18, 19, 20, 13, 12, 3, 2, 1, 8, 7, 17, 16, 15, 14, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[8, 7, 17, 18, 9, 10, 2, 1, 5, 6, 15, 16, 20, 19, 11, 12, 3, 4, 14, 13]
[3, 12, 13, 14, 4, 5, 1, 2, 10, 11, 19, 20, 16, 15, 6, 7, 8, 9, 18, 17]
[3, 4, 5, 1, 2, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 18, 19, 20, 16, 17]
[11, 19, 20, 13, 12, 3, 2, 10, 9, 18, 17, 16, 15, 14, 4, 5, 1, 8, 7, 6]
[4, 14, 15, 6, 5, 1, 2, 3, 12, 13, 20, 16, 17, 7, 8, 9, 10, 11, 19, 18]
[2, 10, 11, 12, 3, 4, 5, 1, 8, 9, 18, 19, 20, 13, 14, 15, 6, 7, 17, 16]
[4, 5, 1, 2, 3, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 19, 20, 16, 17, 18]
[10, 2, 1, 8, 9, 18, 19, 11, 12, 3, 4, 5, 6, 7, 17, 16, 20, 13, 14, 15]
[3, 2, 10, 11, 12, 13, 14, 4, 5, 1, 8, 9, 18, 19, 20, 16, 15, 6, 7, 17]
[7, 8, 1, 5, 6, 15, 16, 17, 18, 9, 10, 2, 3, 4, 14, 13, 20, 19, 11, 12]
[13, 12, 11, 19, 20, 16, 15, 14, 4, 3, 2, 10, 9, 18, 17, 7, 6, 5, 1, 8]
[16, 15, 14, 13, 20, 19, 18, 17, 7, 6, 5, 4, 3, 12, 11, 10, 9, 8, 1, 2]
[15, 16, 17, 7, 6, 5, 4, 14, 13, 20, 19, 18, 9, 8, 1, 2, 3, 12, 11, 10]
[20, 13, 12, 11, 19, 18, 17, 16, 15, 14, 4, 3, 2, 10, 9, 8, 7, 6, 5, 1]
[5, 4, 14, 15, 6, 7, 8, 1, 2, 3, 12, 13, 20, 16, 17, 18, 9, 10, 11, 19]
[10, 11, 12, 3, 2, 1, 8, 9, 18, 19, 20, 13, 14, 4, 5, 6, 7, 17, 16, 15]
[4, 3, 12, 13, 14, 15, 6, 5, 1, 2, 10, 11, 19, 20, 16, 17, 7, 8, 9, 18]
[19, 20, 13, 12, 11, 10, 9, 18, 17, 16, 15, 14, 4, 3, 2, 1, 8, 7, 6, 5]
[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[8, 1, 5, 6, 7, 17, 18, 9, 10, 2, 3, 4, 14, 15, 16, 20, 19, 11, 12, 13]
[18, 9, 8, 7, 17, 16, 20, 19, 11, 10, 2, 1, 5, 6, 15, 14, 13, 12, 3, 4]
[12, 3, 2, 10, 11, 19, 20, 13, 14, 4, 5, 1, 8, 9, 18, 17, 16, 15, 6, 7]
[2, 3, 4, 5, 1, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 17, 18, 19, 20, 16]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]
[9, 8, 7, 17, 18, 19, 11, 10, 2, 1, 5, 6, 15, 16, 20, 13, 12, 3, 4, 14]
[16, 17, 7, 6, 15, 14, 13, 20, 19, 18, 9, 8, 1, 5, 4, 3, 12, 11, 10, 2]
[17, 7, 6, 15, 16, 20, 19, 18, 9, 8, 1, 5, 4, 14, 13, 12, 11, 10, 2, 3]
[1, 5, 6, 7, 8, 9, 10, 2, 3, 4, 14, 15, 16, 17, 18, 19, 11, 12, 13, 20]
[9, 18, 19, 11, 10, 2, 1, 8, 7, 17, 16, 20, 13, 12, 3, 4, 5, 6, 15, 14]
[16, 20, 19, 18, 17, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 1, 5, 4, 3, 2]
[5, 1, 2, 3, 4, 14, 15, 6, 7, 8, 9, 10, 11, 12, 13, 20, 16, 17, 18, 19]
[8, 9, 10, 2, 1, 5, 6, 7, 17, 18, 19, 11, 12, 3, 4, 14, 15, 16, 20, 13]
[13, 20, 16, 15, 14, 4, 3, 12, 11, 19, 18, 17, 7, 6, 5, 1, 2, 10, 9, 8]
[6, 15, 16, 17, 7, 8, 1, 5, 4, 14, 13, 20, 19, 18, 9, 10, 2, 3, 12, 11]
[6, 5, 4, 14, 15, 16, 17, 7, 8, 1, 2, 3, 12, 13, 20, 19, 18, 9, 10, 11]
[7, 6, 15, 16, 17, 18, 9, 8, 1, 5, 4, 14, 13, 20, 19, 11, 10, 2, 3, 12]
[19, 18, 17, 16, 20, 13, 12, 11, 10, 9, 8, 7, 6, 15, 14, 4, 3, 2, 1, 5]
[14, 15, 6, 5, 4, 3, 12, 13, 20, 16, 17, 7, 8, 1, 2, 10, 11, 19, 18, 9]
[17, 18, 9, 8, 7, 6, 15, 16, 20, 19, 11, 10, 2, 1, 5, 4, 14, 13, 12, 3]
[6, 7, 8, 1, 5, 4, 14, 15, 16, 17, 18, 9, 10, 2, 3, 12, 13, 20, 19, 11]
[14, 13, 20, 16, 15, 6, 5, 4, 3, 12, 11, 19, 18, 17, 7, 8, 1, 2, 10, 9]
[20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[7, 17, 18, 9, 8, 1, 5, 6, 15, 16, 20, 19, 11, 10, 2, 3, 4, 14, 13, 12]
[15, 6, 5, 4, 14, 13, 20, 16, 17, 7, 8, 1, 2, 3, 12, 11, 19, 18, 9, 10]
[9, 10, 2, 1, 8, 7, 17, 18, 19, 11, 12, 3, 4, 5, 6, 15, 16, 20, 13, 14]
[2, 1, 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, 17, 18, 19, 20, 13, 14, 15, 16]
[12, 13, 14, 4, 3, 2, 10, 11, 19, 20, 16, 15, 6, 5, 1, 8, 9, 18, 17, 7]
[17, 16, 20, 19, 18, 9, 8, 7, 6, 15, 14, 13, 12, 11, 10, 2, 1, 5, 4, 3]
[18, 17, 16, 20, 19, 11, 10, 9, 8, 7, 6, 15, 14, 13, 12, 3, 2, 1, 5, 4]
[18, 19, 11, 10, 9, 8, 7, 17, 16, 20, 13, 12, 3, 2, 1, 5, 6, 15, 14, 4]
[11, 12, 3, 2, 10, 9, 18, 19, 20, 13, 14, 4, 5, 1, 8, 7, 17, 16, 15, 6]
[15, 14, 13, 20, 16, 17, 7, 6, 5, 4, 3, 12, 11, 19, 18, 9, 8, 1, 2, 10]
[19, 11, 10, 9, 18, 17, 16, 20, 13, 12, 3, 2, 1, 8, 7, 6, 15, 14, 4, 5]
[12, 11, 19, 20, 13, 14, 4, 3, 2, 10, 9, 18, 17, 16, 15, 6, 5, 1, 8, 7]
[20, 16, 15, 14, 13, 12, 11, 19, 18, 17, 7, 6, 5, 4, 3, 2, 10, 9, 8, 1]
[13, 14, 4, 3, 12, 11, 19, 20, 16, 15, 6, 5, 1, 2, 10, 9, 18, 17, 7, 8]
[5, 6, 7, 8, 1, 2, 3, 4, 14, 15, 16, 17, 18, 9, 10, 11, 12, 13, 20, 19]
[14, 4, 3, 12, 13, 20, 16, 15, 6, 5, 1, 2, 10, 11, 19, 18, 17, 7, 8, 9]

다른 그물 단순히 발생할 때마다 교체 ii(입력에서 제 번호 i1을 기반으로).

관련 도전

리더 보드

시리즈의 첫 번째 게시물은 리더 보드를 생성합니다.

답변이 표시되도록하려면 다음 마크 다운 템플릿을 사용하여 모든 답변을 헤드 라인으로 시작하십시오.

## Language Name, N bytes

N제출물의 크기는 어디에 있습니까 ? 당신이 당신의 점수를 향상시킬 경우에, 당신은 할 수 있습니다 를 통해 눈에 띄는에 의해, 헤드 라인에 오래된 점수를 유지한다. 예를 들어 :

## Ruby, <s>104</s> <s>101</s> 96 bytes

(언어는 현재 표시되어 있지 않지만 스 니펫은이를 요구하고 구문 분석하며 향후 언어 별 리더 보드를 추가 할 수 있습니다.)


우리의 토론과 관련하여, 나는 더 명확하게하기 위해 "필수"라는 단어를 추가했습니다. 나는 그것이 내가 이용하고있는 허점을 닫았다 고 생각한다. 여전히, 내가 사용한 접근법 (유효하지는 않지만)이 흥미 롭습니다.
Level River St

이것은 거의 정확히 내 샌드 박스 게시물과 같습니다!
Rɪᴋᴇʀ

@RikerW 그것이 당신이 샌드 박스를 만들 때 생각했던 것입니다. ;) (당시, 내 것이 바로 당신 바로 아래였습니다. 나는 이것이 당신에게 영감을 줄 것이라고 생각했습니다.) 당신은 분명히 훨씬 간단합니다 (아마도 좋은 것입니다).
Martin Ender

당신을 본 적이 없습니다. 이상합니다. 첫 페이지에있는 내용을 모두 읽었다 고 생각했습니다. 그러나 나는 독립적으로 내 것을 만들었습니다.
Rɪᴋᴇʀ

제목이 "d20을 펼치십시오"라고 말하면 안됩니까?
Titus

답변:


7

루비, 160 바이트 (rev B)

Martin Büttner의 제안으로 17 바이트가 절약되었습니다.

->a,n{n.times{k=rand 60
%w{ABCD@GHIJKLMNEFPQRSO PFENOSRQHG@DCMLKJIAB GFPQHIA@DENOSRJKBCML}.map{|b|k.times{a=b.chars.map{|i|a[i.ord-64]}}}
k>29&&a.reverse!
p a}}

루비, 177 바이트 (rev A)

->n,a{n.times{k=rand(60)
h=->b{k.times{|j|a=(0..19).map{|i|a[b[i].ord-64]}}}
h['ABCD@GHIJKLMNEFPQRSO']
h['PFENOSRQHG@DCMLKJIAB']
h['GFPQHIA@DENOSRJKBCML']
k>29&&a.reverse!
p a}}

설명

한면 (3 중), 하나의 정점 (5 중) 및 2 개의 모서리 (2 중)에 대한 회전으로 가능한 모든 방향을 생성 할 수 있습니다. 그러나 얼굴과 가장자리 만이 아닙니다. 축이 올바른 관계를 가져야하고 회전이 올바른 순서로 수행되어야합니다. 그렇지 않으면 이상한 일이 발생할 수 있습니다.

이것이 내가 한 방식입니다 (마틴이 다르게했음을 이해하지만).

사면체의 모든 방향은 다음 세 가지 대칭 연산의 조합으로 생성 할 수 있습니다.

a) 모서리의 중간 점을 통해 오른쪽으로 2 개의 2 중 축을 중심으로 회전 (이는 서로 직각을 이룹니다. 함께 곱하면 나머지 모서리 쌍을 통해 세 번째 2 중 축을 발견합니다.)

b) 정점과면을 통과하는 3 개의 축을 중심으로 2 개의 축에 대해 회전.

정 이십 면체의 대칭은 사면체의 대칭이다. https://en.wikipedia.org/wiki/Icosahedron의 아래 이미지 에서 노란색면과 빨간색면은 두 개의 서로 다른 4 면체 (또는 단일 8 면체)를 정의하는 반면 두 개의 파란색면에 공통 인 모서리는 3 쌍입니다. 직각 (그리고 입방체의면에 놓여 있음)

정 이십 면체의 모든 방향은 위에서 언급 한 대칭 작업과 추가 5 배 작업으로 생성 할 수 있습니다.

여기에 이미지 설명을 입력하십시오

위에서 언급 한 4 개의 축 중 3 개에 대한 회전은 ''마크 사이의 마술 줄로 표시됩니다. 두 번째 2 중 축을 중심으로 한 회전은 다르며 배열을 반전 시켜서 수행 할 수 있습니다 a[].

테스트 프로그램에서 ungolfed :

f=->a,n{
  n.times{                     #Iterate n times, using the result from the previous iteration to generate the next
    k=rand(60)                 #pick a random number

    h=->b{                     #helper function taking a string representing a transformation
      k.times{|j|              #which is performed on a using the number of times according to k
        a=(0..19).map{|i|a[b[i].ord-64]}
      }
    }

    #Rotate about axes k times (one 5-fold, one 3-fold, two 2-fold)
    #The first three axes have coprime rotation orders
    #And the rotations themselves take care of the modulus operation so no need to add it.
    #The second 2-fold rotation is equivalent to reversing the order
    #And is applied to the last 30 numbers as it is not coprime with the first 2-fold rotation.

    h['ABCD@GHIJKLMNEFPQRSO']  #rotate k times about 5-fold axis
    h['PFENOSRQHG@DCMLKJIAB']  #rotate k times about 3-fold axis
    h['GFPQHIA@DENOSRJKBCML']  #rotate k times about 2-fold axis
    k>29&&a.reverse!
    p a
  }
}

z=(1..20).map{|i|i} 
f[z,50]

대체 솔루션 131 바이트 (바이너리 랜덤 워크 접근으로 인해 유효하지 않으며 대략적으로 정확한 분포 만 제공)

->a,n{(n*99).times{|i|s=['@DEFGHIABCMNOPQRJKLS','ABCD@GHIJKLMNEFPQRSO'][rand(2)] 
a=(0..19).map{|i|a[s[i].ord-64]}
i%99==98&&p(a)}}

이것은 스크램블입니다 (루빅스 큐브를 스크램블하는 데 사용되는 프로그램과 유사).

내가 사용하는 특정 회전은 가장 명백한 회전 중 두 가지입니다.

-120도 회전 (첫 번째 다이어그램 당면 1과 20에 대해)

-72도 회전 (첫 번째 다이어그램에서 1,2,3,4,5 및 16,17,18,19,20에 공통 인 정점에 대해)

우리는 동전을 99 번 뒤집으며, 매번 꼬리인지 꼬리인지에 따라이 두 회전 중 하나를 수행합니다.

이들을 결정적으로 교대하면 시퀀스가 ​​상당히 짧아집니다. 예를 들어, 올바른 회전 감지를 사용하면 단 2 주기로 180도 회전 할 수 있습니다.


연산을 선택하기 위해 동전을 뒤집는 것은 균일 분포보다 이항 분포에 더 가까운 것을 산출하는 것처럼 보입니다.
Sparr

상태 공간이 임의의 보행보다 큰 경우에는 @Sparr입니다. 그러나이 경우 6 단계의 무작위 산책은 2 ^ 6 = 64 가능성 (열거에 세지 않았습니다)까지 열 수 있으며 상태 공간은 60 개입니다 .99 단계 이후 (2 ^ 99 다른 경로) 숫자를 생성하는 데 사용되는 PRNG의 단일 샘플만큼 모든 것이 최소한 균일하게 분포되어야합니다.
Level River St

@ MartinBüttner 팁을 주셔서 감사합니다. 작동하는 팁을 적용했습니다. b.map직접 작동하지 않으면 작동해야 b.chars.map합니다 (Ruby 1.9.3을 사용하므로 기계에서 작동하지 않는 IBT이지만 Ideone에서는 작동하는 BTW). 이것은 상당히 절약됩니다. 인쇄 할 수없는 문자의 마법 문자열을 변경하여 -64의지 를 저장한다고 생각하지 않습니다 . 공백과 공간을 생성하여 배열의 문자열 사이의 구분 기호로 %w{}해석 \n합니다. 나는 인쇄 할 수없는 다른 ASCII 문자로 무엇을 할 것인지 전혀 모른다.
Level River St

@Martin 이것은 예상보다 어려웠습니다. 처음에는 코드가 제대로 작동하지 않을 때 당황했습니다. 그리고 잠시 휴식을 취하고 갑자기 2 배 및 3 배 대칭이 동일한 상호 관계를 가져야한다는 것을 깨달았습니다 사면체 (다른 삼각형면을 위해 회전하고있는 삼각형면을 변경해야했습니다.)
Level River St

1
새로 잠금 해제 된 형상 배지를 사용한 첫 번째 사용자가 된 것을 축하합니다 . :)
Martin Ender

2

IA-32 기계 코드, 118 바이트

16 진 덤프 :

60 33 c0 51 8b 74 24 28 8b fa 6a 05 59 f3 a5 e8
21 00 00 00 20 c4 61 cd 6a 33 00 84 80 ad a8 33
32 00 46 20 44 8e 48 61 2d 2c 33 32 4a 00 21 20
a7 a2 90 8c 00 5b b1 04 51 0f c7 f1 83 e1 1f 49
7e f7 51 8b f2 56 8d 7c 24 e0 b1 14 f3 a4 5f 8b
f3 ac 8b ee d4 20 86 cc e3 0a 56 8d 74 04 e0 f3
a4 5e eb ed 59 e2 db 8b dd 59 e2 cc 59 83 c2 14
e2 91 61 c2 04 00

조금 길기 때문에 몇 가지 설명이 먼저 시작됩니다. Level River St 의 기존 답변 과 거의 동일한 알고리즘을 사용했습니다 . 대답을 다르게 만들기 위해 직관적 인 기하학적 의미를 가질 필요는 없지만 다소 편리한 다른 기본 순열을 사용했습니다.

코드는 기본적으로 순열을 생성해야하며 이는 다음을 순차적으로 적용합니다.

  1. 내가 호출하는 차수 3의 순열은 p30 ... 2 회 적용되었습니다.
  2. 내가 호출하는 차수 2의 순열은 q20 또는 1 회 적용됨
  3. 내가 호출하는 차수 5의 순열은 p50 ... 4 회 적용되었습니다.
  4. 내가 호출하는 차수 2의 다른 순열은 p20 번 또는 1 번 적용되었습니다.

이러한 순열에 대해 가능한 많은 선택 사항이 있습니다. 그중 하나는 다음과 같습니다.

p3 = [0   4   5   6   7   8   9   1   2   3  13  14  15  16  17  18  10  11  12  19]
q2 = [4   5   6   7   0   1   2   3  13  14  15  16  17   8   9  10  11  12  19  18]
p5 = [6   7   0   4   5  14  15  16  17   8   9   1   2   3  13  12  19  18  10  11]
p2 = [1   0   7   8   9  10  11   2   3   4   5   6  16  17  18  19  12  13  14  15]

여기에서의 순열은 순차 인덱스가 길기 때문에 런-길이 인코딩으로 압축 될 수 있기 때문에이 선택은 다른 것보다 낫습니다.

난수 생성을 단순화하기 위해 1 ~ 30 범위의 거듭 제곱 (각 순열이 몇 번 적용되는지)을 모두 선택했습니다. 이는 코드 p3자체에 3 배를 곱할 때마다 ID 순열이 되기 때문에 코드에서 추가 작업이 많이 발생 합니다. 그러나 코드는 그렇게 작으며 범위를 30으로 나눌 수 있으면 출력은 균일하게 분포됩니다.

또한, 런-길이 디코딩 동작이 적어도 한 번 수행되도록 전력은 양수 여야한다.

이 코드에는 4 개의 중첩 루프가 있습니다. 개요는 다음과 같습니다.

void doit(int n, uint8_t* output, const uint8_t input[20])
{    
    uint8_t temp[20];

    n_loop: for i_n = 0 ... n
    {
        memcpy(output, input, 20);
        expr_loop: for i_expr = 0 ... 3
        {
            power = rand(1 ... 30);
            power_loop: for i_power = 0 ... power
            {
                memcpy(temp, output, 20);
                output_index = 0;
                perm_loop: do while length > 0
                {
                    index = ...; // decode it
                    length = ...; // decode it
                    memcpy(output + output_index, temp + index, length);
                    output_index += length;
                }
            }
        }
        output += 20;
    }
}

이 의사 코드가 아래의 인라인 어셈블리 코드보다 명확하기를 바랍니다.

_declspec(naked) void __fastcall doit(int n, uint8_t* output, const uint8_t* input)
{
    _asm {
        pushad
        xor eax, eax

        n_loop:
            push ecx

            ; copy from input to output
            mov esi, [esp + 0x28]
            mov edi, edx
            push 5
            pop ecx
            rep movsd

            call end_of_data
#define rl(index, length) _emit(length * 32 + index)
            rl(0, 1)
            rl(4, 6)
            rl(1, 3)
            rl(13, 6)
            rl(10, 3)
            rl(19, 1)
            _emit(0)

            rl(4, 4)
            rl(0, 4)
            rl(13, 5)
            rl(8, 5)
            rl(19, 1)
            rl(18, 1)
            _emit(0)

            rl(6, 2)
            rl(0, 1)
            rl(4, 2)
            rl(14, 4)
            rl(8, 2)
            rl(1, 3)
            rl(13, 1)
            rl(12, 1)
            rl(19, 1)
            rl(18, 1)
            rl(10, 2)
            _emit(0)

            rl(1, 1)
            rl(0, 1)
            rl(7, 5)
            rl(2, 5)
            rl(16, 4)
            rl(12, 4)
            _emit(0)

            end_of_data:
            pop ebx ; load the address of the encoded data
            mov cl, 4

            expr_loop:
                push ecx

                make_rand:
                rdrand ecx
                and ecx, 31
                dec ecx
                jle make_rand

                ; input: ebx => encoding of permutation
                ; output: ebp => encoding of next permutation
                power_loop:
                    push ecx

                    ; copy from output to temp
                    mov esi, edx
                    push esi
                    lea edi, [esp - 0x20]
                    mov cl, 20
                    rep movsb
                    pop edi

                    ; ebx => encoding of permutation
                    ; edi => output
                    mov esi, ebx
                    perm_loop:
                        ; read a run-length
                        lodsb
                        mov ebp, esi

                        _emit(0xd4)             ; divide by 32, that is, split into
                        _emit(32)               ; index (al) and length (ah)
                        xchg cl, ah             ; set ecx = length; also makes eax = al
                        jecxz perm_loop_done    ; zero length => done decoding
                        push esi
                        lea esi, [esp + eax - 0x20]
                        rep movsb
                        pop esi
                        jmp perm_loop

                    perm_loop_done:
                    pop ecx
                    loop power_loop

                mov ebx, ebp
                pop ecx
                loop expr_loop

            pop ecx
            add edx, 20
            loop n_loop

        popad
        ret 4
    }
}

재미있는 구현 세부 사항 :

  • 고급 언어와 같이 들여 쓰기를 사용했습니다. 그렇지 않으면 코드가 이해할 수없는 혼란 일 것입니다
  • I 사용 call이후 및 pop코드에 저장된 액세스 데이터 (부호화 순열)로
  • jecxz명령을 사용하면 실행 길이 디코딩 프로세스의 종료로 0 바이트를 사용할 수 있습니다.
  • 운 좋게도 숫자 30 (2 * 3 * 5)은 거의 2의 거듭 제곱입니다. 이렇게하면 짧은 코드를 사용하여 1 ... 30 범위의 숫자를 생성 할 수 있습니다.

            and ecx, 31
            dec ecx
            jle make_rand
    
  • "범용 분할"명령 ( aam)을 사용하여 바이트를 비트 필드 (3 비트 길이 및 5 비트 인덱스)로 분리합니다. 운이 좋으면 코드의 해당 위치 cl = 0에서 두 가지 측면의 이점을 얻습니다.xchg

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.