Wyjmij potasowaną talię za pomocą losowych danych wejściowych

9

Wejście wyjście:

Wejście : jednolicie losowy, nieskończenie długi ciąg „0” i „1”, wzięty ze standardowego wejścia. Zakłada się, że ciąg znaków jest naprawdę losowy, a nie pseudolosowy. Jest jednolity, ponieważ każda postać może być równa „0” lub „1”.

Ostrożny! Dane wejściowe są nieskończenie długie, więc nie można przechowywać wszystkiego w pamięci za pomocą funkcji takiej jak raw_input () w pythonie. Jeśli się nie mylę, golfscript zakończy się niepowodzeniem z nieskończonym wejściem, ponieważ wypycha całe wejście na stos przed uruchomieniem.

Wyjście : jednolicie losowo tasowana standardowa talia, bez żartów. Jest jednolity, ponieważ wszystkie zamówienia są jednakowo prawdopodobne.

Każda karta na wyjściu ma swoją rangę, A, 2-9, T, J, Q lub K połączoną z jej kolorem, c, d, h lub s. Na przykład 10 pik toTs

Karty z talii powinny być oddzielone spacjami.

Nie możesz używać wbudowanych losowych bibliotek lub funkcji, ponieważ nie są one tak naprawdę losowe, tylko pseudolosowe.

Przykładowe dane wejściowe

Możesz użyć następującego skryptu python, aby potokować dane wejściowe do swojego programu:

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

Jeśli zapiszesz skrypt jako rand.py, przetestuj swój program za pomocą python rand.py | your_program

W Pythonie 3 działa zgodnie z oczekiwaniami, ale w Pythonie 2.7 pojawia się komunikat o błędzie po wyjściu mojego programu, ale dopiero po wszystkim, więc po prostu zignoruj ​​komunikat o błędzie.

Przykładowe dane wyjściowe:

Oto, jak należy wydrukować talię, jeśli zdarzyło się, że została przetasowana w uporządkowanej kolejności:

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

Punktacja:

To jest golf golfowy. Najkrótszy kod wygrywa.

Przykładowy program:

Oto rozwiązanie Python 2.7, nie gra w golfa.

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))),
pudełko kartonowe
źródło
3
„Jeśli się nie mylę, golfscript nie powiedzie się z nieskończonym wejściem, ponieważ wypycha cały plik wejściowy na stos przed uruchomieniem.” To jeden ze sposobów na wyeliminowanie go z gry.
dmckee --- były moderator kociak
Jestem trochę zdezorientowany, wybacz mi. Co dane wejściowe mają wspólnego z faktycznym tasowaniem talii? Być może potrzebuję tylko trochę wyjaśnienia.
jdstankosky
1
Nie możesz używać pseudolosowych funkcji w swoim kodzie, więc musisz użyć danych wejściowych (które, jak zakładamy, są naprawdę losowe), aby wygenerować losowość. Na przykład w pythonie możesz użyć (sys.stdin.read (1) == '1'), aby uzyskać losową wartość logiczną, ale nie możesz użyć (random.randint (0,1) == 1), ponieważ to tylko pseudolosowe.
cardboard_box

Odpowiedzi:

7

Rubin, 89 87 znaków

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

Edycja: poprzednia wersja

l=*0..51;(l-=[i=l[gets(6).to_i 2]];i&&$><<'A23456789TJQK'[i/4]+'cdhs'[i%4]+' ')while l[0]
Howard
źródło
3

Python 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)

Wyjaśnienie:

Nieużywane karty są przechowywane w D. To po prostu pobiera następny prawidłowy losowy indeks ze strumienia wejściowego i wyskakuje ten element z D.

Jeśli czegoś mi nie brakuje, nie powinno być stronniczości. Skrypt wyrzuci wszelkie nieprawidłowe indeksy> len(D), ale nie spowoduje to odchylenia dla niższych liczb, ponieważ każde kolejne pop zmniejszy indeks każdego elementu obok i.

scleaver
źródło
Więc odrzucasz większość (nieskończonych) losowych danych wejściowych? Czy przestajesz tasować, gdy nie masz już „nieużywanych” kart?
Leigh
3

Perl, 80 znaków

oto kolejna implementacja, która nie cierpi z powodu błędu i ma dwa znaki krótsze:

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

stara implementacja (82 znaków):

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

stary opis wdrożenia:

# 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;
}
ardnew
źródło
Ciekawe: czy ktoś może pokazać, że takie podejście daje każdą talię kart z takim samym prawdopodobieństwem?
Howard
2
Jeśli czytam to prawo (IANAPH), przypisuje on losowe „wagi” każdej karcie, a następnie sortuje według wagi. Gdy dwie karty mają przypisany taki sam ciężar, zostaną one pozostawione w kolejności do sort, co spowoduje odchylenie w kierunku kolejności alfabetycznej.
stoisko
masz rację, @boothby. sortowanie pozostawia to rozwiązanie z uprzedzeniem w przypadku, gdy wiele kart ma tę samą „wagę”. nie można również zagwarantować, że to rozwiązanie kiedykolwiek przyniesie skutek. Dodam opis, jak to działa, aby ktoś mądrzejszy ode mnie mógł to przeanalizować
nowy
Jest całkowicie w porządku, jeśli niektóre dane wejściowe powodują, że program nigdy się nie kończy, o ile prawdopodobieństwo, że program się zakończy, zbliża się do 1, gdy czas zbliża się do nieskończoności. Przykładowy program nigdy nie kończy się na wejściu wszystkich „1”. Jestem prawie pewien, że w rzeczywistości niemożliwe jest uzyskanie jednolitej losowości, wiedząc, że program kończy się po odczytaniu określonej liczby bitów.
cardboard_box
1
Jak wybrać jednolicie losową liczbę od 1 do 3 o skończonej liczbie bitów? Musisz to zrobić pod koniec losowania Fishera-Yatesa, a silnia (52) jest podzielna przez 3, więc dzieli ten sam problem.
cardboard_box
3

C, 197 178 161 znaków

EDYCJA : Używając nowej funkcji losowej, która jest znacznie krótsza - odczytuje 4-cyfrową liczbę całkowitą si używa s%64. Każda 6-cyfrowa liczba dziesiętna złożona tylko z 0 i 1, %64daje unikalny wynik, więc losowość jest dobra.
Takie podejście zużywa znacznie więcej losowych bitów, ale jest znacznie krótsze.

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;
}

Podstawowa logika jest prosta - zainicjuj tablicę 52 liczb całkowitych za pomocą 0..51, losuj (losowo zamień element x na inny z zakresu 0..x), sformatuj wydruk (n / 4 = pozycja, n% 4 = kolor) .
Jedna pętla, która działa 104 razy, wykonuje inicjalizację (pierwsze 52 przebiegi), tasowanie i drukowanie (ostatnie 52 przebiegi).
Liczba losowa jest generowana przez ciągnięcie nlosowych bitów, dopóki 1<<nnie osiągnie przynajmniej pożądanego maksimum. Jeśli wynik jest większy niż maksymalny - spróbuj ponownie.

ugoren
źródło
To skomplikowane s>7?"ATJQK"[s-8]:s+50jest dłuższe niż proste "A23456789TJQK"[s]. Po drugie możesz użyć t/4i t%4zamiast t%13i t/13.
Howard
Nie musisz nadal wkładać z tpowrotem do tablicy podczas wysyłania
l4m2
3

powłoka unix ~ 350

Nie jest to ani krótkie, ani ładne, ani wydajne, ale zastanawiałem się, jak trudno byłoby to zrobić za pomocą standardowych narzędzi powłoki Unix.

Ta odpowiedź dzieli nieskończony ciąg binarny na 6 bitów i wybiera tylko te, które są w prawidłowym zakresie (1-52), tutaj nieskończony ciąg binarny jest symulowany przez urandom i xxd:

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

Siekanie i selekcja odbywa się za pomocą fold, sed i bc:

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

Powoduje to utworzenie linii takich jak:

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

Które można skierować do bc.

Z tego strumienia liczb sekwencja talii jest wybierana w następujący sposób (używam zsh, ale większość współczesnych powłok powinna być przystosowana do tego):

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

Losowa sekwencja numerów musi teraz zostać zmieniona na nazwy kart. Sekwencję nazw kart można łatwo wygenerować za pomocą GNU równolegle:

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

Łączenie danych wyjściowych z dwóch ostatnich poleceń z wklejaniem i sortowaniem według liczb:

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

Całość jako jeden monstrualny jednowarstwowy (testowany tylko w 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' ' '

Edytuj - dodana wersja bash

Oto wersja, która działa w bash. Usunąłem powłokę, { }a indeksy tablic są oparte na zerach. Pustość tablicy jest sprawdzana za pomocą rozszerzania parametrów, nieco bardziej wydajna, a także stosowana w powyższym przykładzie.

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
Thor
źródło
2

K&R c - 275

  • v3 Indeks bezpośrednio do literałów łańcuchowych
  • v2 Sugestia luser droog w komentarzach do użycia ciągów i zastąpienia pozostałych charliterałów intliterałami

Gra w golfa:

#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);}}

Bardzo brutalna siła tutaj. Właśnie odczytałem dziewięć bitów z wejścia, aby utworzyć minimalne wyjście RNG i dokonałem zwykłej redukcji modułu przerysowania, jeśli nieużywane wartości na końcu, aby uzyskać jednolity wynik, aby zasilić losowe wybieranie.

Ta wersja bez gry w golfa różni się tym, że pobiera dane wejściowe z /dev/urandomopisanego formatu wejściowego, a nie z niego.

#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(' ');
  }
}
dmckee --- były kot moderator
źródło
+1 Mam wiele do nauczenia się. BTW, dlaczego nie "TJQKA"i "cdhs"?
luser droog
O. Dobrze. ints. Rozumiem. Może nadal warto, aby zapisać wszystkie znaki interpunkcyjne. Może nawet czynnik charout of getchara putcharz szalonej postaci pasty makro ...
luser Droog
1
Makropolecenia muszą dużo zyskać, ponieważ muszą zaczynać się #define N i kończyć nową linią, która liczy się jako postać i to 11, plus kawałek, który zastępujesz. Z pewnością jest kilka innych znaków zastępujących niektóre lub wszystkie literały znakowe literałami int, ale tutaj jest już późno ... może zrobię to innym razem.
dmckee --- były moderator kociak
@luserdroog Clearer udał się teraz. Oczywiście łańcuchy są lepsze - choć musisz określić typ - ponieważ znaki są tylko krótkimi liczbami całkowitymi. Dodatkowo mogę je łączyć, a podstawienia ASCII za jednym pociągnięciem.
dmckee --- były moderator kociak
0

PHP, 158 znaków

Dodano nowe linie, aby blok kodu nie zyskał pasków przewijania, można je bezpiecznie usunąć.

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));

Zanim dostanę polecenie dodania a <?php, daj znać, że możesz łatwo wywołać PHP bez tego znacznika, używając:cat golf.php | php -a

Grał w golfa i skomentował:

// 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)
);

Występują dwa oczekiwane błędy, które nie wpływają na wynik programu.

Po pierwsze, ponieważ $anie został zainicjowany, ale wartość NULL jest konwertowana na 0, a program kontynuuje działanie.

Drugi polega na tym, że strumień znaków wydaje się skądś otrzymywać nową linię, nawet jeśli nie jest dostarczony (dobry stary PHP), i jest to niezdefiniowany indeks w tablicy. Jest to ostatni znak wejścia i nie wpływa na wynik.

Leigh
źródło