무작위 입력을 사용하여 셔플 데크 출력


9

입출력:

입력 : stdin에서 가져온 균일하게 임의의 무한 길이의 '0'과 '1'. 문자열은 의사 난수가 아니라 실제로 무작위로 가정됩니다. 각 문자가 '0'또는 '1'일 가능성이 균일합니다.

꼼꼼한! 입력은 무한히 길기 때문에 파이썬에서 raw_input ()과 같은 함수를 사용하여 메모리에 모두 저장할 수는 없습니다. 내가 실수하지 않으면 골프 스크립트는 실행하기 전에 전체 입력을 스택에 푸시하기 때문에 무한 입력으로 실패합니다.

출력 : 조커없이 균일하게 무작위 섞인 표준 데크. 모든 주문이 똑같이 가능하다는 점에서 균일합니다.

출력의 각 카드는 등급, A, 2-9, T, J, Q 또는 K이며 정장, c, d, h 또는 s에 연결됩니다. 예를 들어, 10 개의 스페이드는Ts

갑판의 카드는 공백으로 분리해야합니다.

내장 무작위 라이브러리 또는 함수는 실제로 무작위가 아니고 의사 난수이기 때문에 사용할 수 없습니다.

입력 예

다음 파이썬 스크립트를 사용하여 입력을 프로그램에 파이프 할 수 있습니다.

import sys, random
try:
    while True:
        sys.stdout.write(str(random.randint(0,1)))
except IOError:
    pass

스크립트를 rand.py로 저장하면 다음을 사용하여 프로그램을 테스트하십시오. python rand.py | your_program

파이썬 3에서는 예상대로 실행되지만 파이썬 2.7에서는 프로그램 출력 후에 오류 메시지가 표시되지만 모든 작업이 완료된 후에 만 ​​오류 메시지가 표시되므로 오류 메시지를 무시하십시오.

출력 예 :

데크가 정렬 순서대로 섞인 경우 데크를 인쇄하는 방법은 다음과 같습니다.

Ac 2c 3c 4c 5c 6c 7c 8c 9c Tc Jc Qc Kc Ad 2d 3d 4d 5d 6d 7d 8d 9d Td Jd Qd Kd Ah 2h 3h 4h 5h 6h 7h 8h 9h Th Jh Qh Kh As 2s 3s 4s 5s 6s 7s 8s 9s Ts Js Qs Ks

채점 :

이것은 코드 골프입니다. 가장 짧은 코드가 승리합니다.

프로그램 예 :

골프가 아닌 파이썬 2.7 솔루션이 있습니다.

import sys
def next():
    return int(sys.stdin.read(1))==1
def roll(n):
    if n==1:
        return 0
    if n%2==0:
        r=roll(n/2)
        if next():
            r+=n/2
        return r
    else:
        r=n
        while(r==n):
            r=roll(n+1)
        return r
deck = [rank+suit for suit in 'cdhs' for rank in 'A23456789TJQK']
while len(deck)>0:
    print deck.pop(roll(len(deck))),

3
"실수하지 않으면 골프 스크립트는 실행하기 전에 전체 입력을 스택으로 푸시하기 때문에 무한 입력으로 실패합니다." 글쎄, 그것이 달리기를 피하는 한 가지 방법입니다.
dmckee ---

조금 혼란스러워 용서하십시오. 입력이 실제 데크 셔플 링과 어떤 관련이 있습니까? 아마도 나는 약간의 설명이 필요합니다.
jdstankosky

1
코드에서 의사 랜덤 함수를 사용할 수 없으므로 임의성을 생성 하기 위해 입력 ( 실제로 랜덤 하다고 가정)을 사용해야 합니다. 예를 들어, 파이썬에서는 (sys.stdin.read (1) == '1')을 사용하여 임의의 부울을 얻을 수 있지만 (random.randint (0,1) == 1)을 사용할 수는 없습니다. 그것은 의사 난수 일뿐입니다.
cardboard_box

답변:


7

루비, 89 87 자

l=*0..51;l.map{l-=[i=l[gets(6).to_i 2]||redo];$><<'A23456789TJQK'[i/4]+'cdhs'[i%4]+' '}

편집 : 이전 버전

l=*0..51;(l-=[i=l[gets(6).to_i 2]];i&&$><<'A23456789TJQK'[i/4]+'cdhs'[i%4]+' ')while l[0]

3

파이썬 122

import sys
D=[R+S for S in'cdhs'for R in'A23456789TJQK']
while(D):
    x=int(sys.stdin.read(6),2)
    if x<len(D):print D.pop(x)

설명:

사용하지 않는 카드는 D에 저장됩니다. 이것은 입력 스트림에서 다음으로 유효한 임의의 인덱스를 가져와 해당 요소를 D에서 팝합니다.

내가 빠진 것이 아니라면 편견이 없어야합니다. 이 스크립트는 유효하지 않은 인덱스>를 버립니다 len(D). 그러나 연속 된 각 팝이 i보다 지난 각 요소의 인덱스를 줄이므로 더 적은 수의 편향이 발생하지 않습니다.


따라서 대부분의 (무한한) 임의 입력을 무시합니까? "사용하지 않은"카드가 없으면 셔플을 중단합니까?
Leigh

3

펄, 80 자

다음은 편견을 겪지 않고 두 문자가 더 짧은 다른 구현입니다.

$/=1x9;$_=A23456789TJQK;s/./$&s$&c$&d$&h/g;%h=map{<>.$_,"$_ "}/../g;say values%h

이전 구현 (82 자) :

$/=1x9;$_=A23456789TJQK;s/./$&s$&c$&d$&h/g;say map/..$/&&$&.$",sort map<>.$_,/../g

이전 구현 설명 :

# set input record separator (how internal readline() delimits lines) to "11111111"
$/ = 1x9; 

# constructs a string representation of all 52 cards: "AsAc(...)KdKh"
$_ = A23456789TJQK; s/./$&s$&c$&d$&h/g;

# for each pair of characters (each card) in the string $_
foreach $card (/../g)
{
    # read from STDIN until $/ is found (this may NEVER occur!), which
    # results in a random string of 1s and 0s
    $weight = <>; 

    # append the card identifier onto the random string
    $card = $weight . $card;

    # add this new card identifier to a new list
    push @cards, $card;
}

# sort the cards with their random string prefix
sort @cards;

# for each card in the "randomly sorted" list
foreach $card (@cards)
{
    # capture the final two characters from the card (the rank and suit), 
    # and append a space onto them
    $card =~ /..$/;  
    $card = $card . $";

    print $card;
}

궁금한 점이 있습니다. 누구나이 방법이 같은 확률로 각 카드 덱을 생성한다는 것을 보여줄 수 있습니까?
Howard

2
이 권한 (IANAPH)을 읽고 있으면 각 카드에 임의의 '무게'를 할당 한 다음 무게를 기준으로 정렬합니다. 두 장의 카드에 같은 무게가 할당되면 순서대로 정렬되어 sort알파벳 순서로 편향됩니다.
boothby

당신은 맞습니다, @boothby. 여러 카드가 동일한 "무게"를 갖는 경우 정렬은이 솔루션에 치우침을 남깁니다. 또한이 솔루션이 결과를 보장 할 수는 없습니다. 나보다 똑똑한 사람이 분석 할 수 있도록 작동 방식에 대한 설명을 추가하겠습니다.
ardnew

시간이 무한대에 가까워 질 때 프로그램이 종료 될 확률이 1에 가까우면 일부 입력으로 인해 프로그램이 종료되지 않으면 완벽하게 괜찮습니다. 예제 프로그램은 모든 '1'의 입력에서 종료되지 않습니다. 특정 비트 수를 읽은 후에 프로그램이 종료된다는 것을 알면서도 균일 한 무작위성을 얻는 것이 실제로 불가능하다고 확신합니다.
cardboard_box

1
유한 한 비트 수로 1에서 3 사이의 균일 한 난수를 어떻게 선택할 수 있습니까? Fisher-Yates 셔플이 끝날 무렵에 그렇게해야하며 factorial (52)는 3으로 나눌 수 있으므로 동일한 문제를 공유합니다.
cardboard_box

3

C, 197 개 (178) 161 문자

편집 : 훨씬 짧은 새로운 임의의 함수 사용-4 자리 정수를 s읽고 사용합니다 s%64. 0과 1로만 구성된 각 6 자리 10 진수 %64는 고유 한 결과를 가져 오므로 임의성이 좋습니다.
이 방법은 훨씬 더 많은 임의의 비트를 소비하지만 상당히 짧습니다.

B[52],t,s,i=104;
r(){scanf("%6d",&s);s%=64;s>i&&r();}
main(){
    for(;i--;)B[i%52]=i<52
        ?r(),t=B[s],B[s]=B[i],printf("%c%c\n","23456789ATJQK"[t/4],"cdhs"[t%4]),t
        :i-52;
}

기본 논리는 간단합니다-0..51, 셔플 (임의로 x 요소를 0..x 범위의 다른 요소로 대체), 52 형식의 배열을 초기화하고 인쇄 형식 (n / 4 = rank, n % 4 = suit) .
104 회 실행되는 하나의 루프는 초기화 (최초 52 회 실행), 셔플 링 및 인쇄 (마지막 52 회 실행)를 수행합니다.
난수 는 적어도 원하는 최대 값이 n될 때까지 난수 비트 를 가져 와서 생성됩니다 1<<n. 결과가 최대 값보다 큰 경우 다시 시도하십시오.


이 복잡한 s>7?"ATJQK"[s-8]:s+50것은 단순보다 길다 "A23456789TJQK"[s]. 두 번째로는 사용할 수 t/4t%4대신 t%13t/13.
Howard

t출력 할 때 어레이 에 다시 넣을 필요가 없습니다
l4m2

3

유닉스 쉘 ~ 350

이것은 짧거나 예쁘지 않거나 효율적이지는 않지만 표준 유닉스 쉘 유틸리티 로이 작업을 수행하는 것이 얼마나 어려운지 궁금했습니다.

이 답변은 무한 이진 문자열을 6 비트 길이로 잘라 내고 올바른 범위 (1-52)에있는 것만 선택합니다. 여기서 무한 이진 문자열은 urandom 및 xxd로 시뮬레이션됩니다.

</dev/urandom xxd -b | cut -d' ' -f2-7 | tr -d ' \n'

도마와 선택은 접기, sed 및 bc로 수행됩니다.

random_source | {echo ibase=2; cat | fold -w6 | sed -r 's/^/if(/; s/([^\(]+)$/\1 <= 110100 \&\& \1 > 0) \1/'}

이것은 다음과 같은 라인을 생성합니다 :

if(101010 <= 110100 && 101010 > 0) 101010

bc로 지시 될 수 있습니다.

이 일련의 숫자에서 데크의 순서는 다음과 같이 선택됩니다 (zsh를 사용하고 있지만 대부분의 현대 쉘은 이에 적합해야합니다).

deck=({1..52})
seq_of_numbers | while read n; do 
  if [[ -n $deck[n] ]]; then 
    echo $n; deck[n]=""
    [[ $deck[*] =~ "^ *$" ]] && break
  fi
done

무작위 숫자 순서는 이제 카드 이름으로 변경해야합니다. 카드 이름 순서는 GNU 병렬로 쉽게 생성됩니다.

parallel echo '{2}{1}' ::: c d s h ::: A {2..9} T J Q K

마지막 두 명령의 출력을 붙여 넣기 및 숫자 정렬과 결합 :

paste random_deck card_names | sort -n | cut -f2 | tr '\n' ' '

하나의 괴물 같은 하나의 라이너 (zsh에서만 테스트 됨) :

paste \
  <(deck=({1..52}); \
    </dev/urandom xxd -b | cut -d' ' -f2-7 | tr -d ' \n' |
      {echo ibase=2; fold -w6 | sed -r 's/^/if(/; s/([^\(]+)$/\1 <= 110100 \&\& \1 > 0) \1/'} | 
      bc | 
      while read n; do 
        if [[ -n $deck[n] ]]; then 
          echo $n; deck[n]=""
          [[ -z ${${deck[*]}%% *} ]] && break
        fi
      done) \
  <(parallel echo '{2}{1}' ::: c d s h ::: A {2..9} T J Q K) | 
sort -n | cut -f2 | tr '\n' ' '

편집-bash 버전 추가

다음은 bash에서 작동하는 버전입니다. 쉘에서 제거했으며 { }배열 인덱스는 0을 기준으로합니다. 배열의 공허함은 매개 변수 확장을 통해 확인되며 약간 더 효율적이며 위의 예에서도 채택되었습니다.

paste \
  <(deck=($(seq 52)); \
    </dev/urandom xxd -b | cut -d' ' -f2-7 | tr -d ' \n' | 
      (echo ibase=2; fold -w6 | sed -r 's/^/if(/; s/([^\(]+)$/\1 <= 110100 \&\& \1 > 0) \1/') | 
        bc | 
        while read n; do 
          if [[ -n ${deck[n-1]} ]]; then 
            echo $n
            deck[n-1]=""
            [[ -z ${deck[*]%% *} ]] && break
          fi
        done \
  ) \
  <(parallel echo '{2}{1}' ::: c d s h ::: A {2..9} T J Q K) | 
sort -n | cut -f2 | tr '\n' ' '; echo

2

K & R c-275

  • v3 문자열 리터럴로 직접 색인
  • v2를 사용하는 문자열에 코멘트에 LUSER의 droog에서 제안 교체 나머지 char와 리터럴 int리터럴

골프 :

#define F for(i=52;--i;)
#define P putchar 
M=1<<9-1,i,j,k,t,v,s,a[52];r(){t=0,j=9;while(--j)t=t<<1|(getchar()==49);
return t;}main(){F a[i]=i;F{k=i+1;do{j=r();}while(j>M/k*k-1);j%=i;t=a[i];
a[i]=a[j];a[j]=t;}F{s=a[i]&3;v=a[i]>>2;P(v>7?"TJQKA"[v-8]:v+50);
P("cdhs"[s]);P(32);}}

여기에 거의 무차별 한 힘이 있습니다. 입력에서 9 비트를 읽고 최소 RNG 출력을 형성하고, 선택 셔플에 전원을 공급하기 위해 균일 한 출력을 얻기 위해 일반적으로 사용하지 않는 값을 최종 계수로 줄입니다.

이 골프화되지 않은 버전은 /dev/urandom설명 된 입력 형식이 아닌 입력을받는 점이 다릅니다 .

#include <stdio.h>
M=1<<8-1, /* RANDMAX */
  i, j, k, /* counters */
  t, /* temporary for swapping, and accumulating */
  a[52]; /* the deck */
r(){ /* limited, low precision rand() that depends on a random stream
    of '0' and '1' from stdin */
  t=0,j=9;
  while(--j)t=t<<1|(getchar()&1);
  return t;
}
main(){
  for(i=52;--i;)a[i]=i;  /* initialize the deck */
  for(i=52;--i;){
    /*  printf("shuffling %d...\n",i); */
    k=i+1;
    do { /* draw *unifromly* with a a-unifrom generator */
      j=r(); 
      /* printf("\t j=0x%o\n",j); */
    }while(j>M/k*k-1); /* discard values we can't mod into evently */
    j%=i;
    t=a[i];a[i]=a[j];a[j]=t; /* swap */
  }
  for(i=52;--i;){ /* output the deck */
    j=a[i]&3;
    k=a[i]>>2;
    putchar(k>7?"TJQKA"[k-8]:k+'2');
    putchar("cdhs"[j]);
    putchar(' ');
  }
}

+1 배울 것이 많다. BTW, 왜 안 "TJQKA""cdhs"?
luser droog

오. 권리.int에스. 알겠습니다 여전히 모든 구두점을 저장하는 것이 좋습니다. 요인도 있습니다 char밖으로 getcharputchar미친 반죽 매크로를 ...
LUSER는 droog

1
매크로 대체 #define N 는 문자로 간주 되는 개행 으로 시작 하고 11로 끝나고 교체 할 비트 와 함께 끝나야 하기 때문에 많은 것을 얻을 필요가 있습니다. 문자 리터럴의 일부 또는 전부를 int 리터럴로 바꾸는 데 분명히 몇 가지 문자가 더 있지만 여기에 늦었습니다. 또 다른 시간에 할 것입니다.
dmckee --- 전 운영자 고양이 새끼

@luserdroog Clearer가 지금 향했습니다. 물론 문자는 짧은 정수이기 때문에 문자열은 유형을 지정해야하지만 더 좋습니다. 또한 나는 그것들을 결합 할 수 있으며 ASCII 대체는 한 번에 많은 스트로크를 얻습니다.
dmckee --- 전 운영자 고양이 새끼

0

PHP, 158 자

스크롤 바를 얻는 코드 블록을 멈추기 위해 줄 바꿈이 추가되어 안전하게 제거 할 수 있습니다.

for($i=52,$x='shdcKQJT98765432A';$i--;$c[]=$x[4+$i%13].$x[$i/13]);
while(ord($i=fgetc(STDIN)))$c[$i]^=$c[$a]^=$c[$i]^=$c[$a=2+($a+++$i)%50];
die(join(' ',$c));

을 추가하라는 지시를 받기 전에이 <?php태그를 사용하지 않고 PHP를 매우 쉽게 호출 할 수 있음을 알려주세요.cat golf.php | php -a

골퍼 해제 및 댓글 달기 :

// Abuse of the for construct to save a bit of space, and to make things more obscure looking in general.
for (
    // Card suit and number are reversed because we're using a decrementor to count
    // down from 52, instead of up to 52
    $i = 52,
    $x = 'shdcKQJT98765432A';
    // Condition AND per-loop decrement
    $i--;
    // Add a new element to the array comprising of $i mod 13 + 4 (to skip suit ids)
    // followed by simply $i divided by 13 to pick a suit id.
    $c[] =
        $x[4 + $i % 13] .
        $x[$i / 13]
);

while(

    // Assignment inside the condition, a single character from input.
    ord($i = fgetc(STDIN))
)
    // In-place swap. Shorter than using a single letter temporary variable.
    // This is the pseudo-random shuffle.
    $c[$i] ^=
    $c[$a] ^=
    $c[$i] ^=
    $c[
        // We use the input (0 or 1) to identify one of two swap locations at the
        // start of the array. The input is also added to an accumulator (to make
        // the increments "random") that represents a swap destination.
        $a = 2 + ($a++ + $i) % 50
    ];

// Dramatic way of doing "echo" in the same space.
die(
    join(' ', $c)
);

프로그램 출력에 영향을 미치지 않는 두 가지 예상 오류가 있습니다.

첫 번째는 $a초기화되지 않았지만 NULL은 0으로 변환되고 프로그램은 계속됩니다.

두 번째는 문자 스트림이 제공되지 않은 경우에도 (좋은 PHP) 문자 스트림이 어딘가에서 개행을 얻는 것처럼 보이며 이는 배열에서 정의되지 않은 인덱스입니다. 입력의 마지막 문자이며 출력에 영향을 미치지 않습니다.

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