Виведіть перемішану колоду за допомогою випадкового введення


9

Введення-виведення:

Введення : Рівномірний випадковий, нескінченно довгий рядок '0 і' 1, узятий із stdin. Рядок вважається справді випадковим, а не псевдовипадковим. Він рівномірний тим, що кожен символ з однаковою ймовірністю буде "0" або "1".

Обережно! Вхід нескінченно довгий, тому ви не можете зберігати все це в пам'яті, використовуючи функцію raw_input () в python. Якщо я не помиляюся, golfscript вийде з ладу з нескінченним введенням, оскільки він натискає весь вклад на стек перед запуском.

Вихід : рівномірно випадкова перетасована стандартна колода, без жартів. Єдина, що всі замовлення однаково ймовірні.

Кожна карта на виході - це ранг, A, 2-9, T, J, Q або K, поєднаний з її костюмом, c, d, h або s. Наприклад, 10 пік єTs

Карти колоди повинні бути розділені пробілами.

Ви не можете використовувати вбудовані випадкові бібліотеки чи функції, оскільки вони не є справді випадковими, лише псевдовипадковими.

Приклад введення

Ви можете використовувати наступний скрипт python для передачі даних у програму:

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

Якщо ви збережете скрипт як rand.py, протестуйте програму python rand.py | your_program

У python 3 він працює як очікувалося, але в python 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

Оцінка:

Це кодовий гольф. Найкоротший код виграє.

Приклад програми:

Ось рішення python 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
"Якщо я не помиляюсь, golfscript вийде з ладу з нескінченним введенням, оскільки він виштовхує весь вклад на стек перед запуском." Ну, це один із способів зняти це з ходу.
dmckee --- кошеня колишнього модератора

Я трохи розгублений, пробачте. Яке вхідне відношення має до фактичного переміщення колоди? Можливо, мені просто потрібно трохи уточнити.
jdstankosky

1
Ви не можете використовувати псевдовипадкові функції у своєму коді, тому вам потрібно використовувати вхід (який, як ми вважаємо, справді випадковий), щоб генерувати випадковість. Наприклад, у python ви можете використовувати (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), але це не призводить до зміщення менших чисел, оскільки кожен наступний поп зменшить індекс кожного минулого елемента, ніж я.


Отже, ви відкинете більшу частину (нескінченного) випадкового введення? Тобто ви перестаєте перетасовувати, як тільки у вас немає "невикористаних" карт?
Лі

3

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

Просто цікаво: чи може хтось показати, що такий підхід виробляє кожну колоду карт з однаковою ймовірністю?
Говард

2
Якщо я читаю це право (IANAPH), він призначає кожній картці випадкові «ваги», а потім сортує за вагою. Якщо дві картки мають однакову вагу, вони залишатимуться в порядку sort, що призводить до зміщення алфавітного порядку.
кабінка

ви праві, @boothby. сортування залишає це рішення з ухилом у випадку, якщо кілька карт мають однакову "вагу". також не можна гарантувати, що це рішення коли-небудь дасть результат. Я додам опис того, як це працює, щоб хтось розумніший за мене міг його проаналізувати
ardnew

Це цілком добре, якщо деякі введення змушують програму ніколи не припинятися, доки ймовірність того, що програма припиняється, наближається до 1, коли час наближається до нескінченності. Прикладна програма ніколи не закінчується на вході всіх '1'. Я впевнений, що насправді неможливо отримати рівномірну випадковість, знаючи, що ваша програма закінчується після певної кількості прочитаних бітів.
cardboard_box

1
Як можна вибрати рівномірно випадкове число між 1 і 3 з кінцевою кількістю біт? Вам потрібно буде це зробити в кінці перетасовки Фішера-Йейта, а факториал (52) ділиться на 3, тому він поділяє ту саму проблему.
cardboard_box

3

C, 197 178 161 символів

EDIT : Використання нової випадкової функції, яка набагато коротша - читає чотиризначне ціле число sта використовує s%64. Кожне 6-розрядне десяткове число, що складається лише з 0 і 1, %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;
}

Основна логіка проста - ініціалізуйте масив з 52 ints з 0..51, перетасуйте (довільно замініть елемент x іншим з діапазону 0..x), друкуйте у форматі (n / 4 = rank, n% 4 = пристосування) .
Один цикл, який працює 104 рази, робить ініціалізацію (перші 52 запуски), переміщення та друк (останні 52 запуски).
Випадкове число генерується шляхом витягування nвипадкових бітів, поки не 1<<nстане хоча б бажаний максимум. Якщо результат більше максимального - повторіть спробу.


Це складне s>7?"ATJQK"[s-8]:s+50довше, ніж просте "A23456789TJQK"[s]. По-друге, ви можете використовувати t/4і t%4замість t%13і t/13.
Говард

Не потрібно все-таки tповертати назад в масив при виведенні
l4m2

3

оболонка unix ~ 350

Це не є коротким чи гарним, а також не ефективним, проте мені було цікаво, як важко це зробити за допомогою стандартних утиліт оболонки Unix.

Ця відповідь розбиває нескінченний бінарний рядок на 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

Яке може бути спрямоване на до н.е.

З цього потоку чисел послідовність колоди вибирається так (я використовую 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. Я видалив { }індекси оболонки, а індекси масиву базуються на нулі. Порожнеча масиву перевіряється розширенням параметрів, трохи ефективнішим, а також прийнятим у наведеному вище прикладі.

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

Тут досить жорстока сила. Я просто прочитав дев'ять біт з вхідного сигналу, щоб сформувати мінімальний вихід 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 У мене є чому навчитися. До речі, чому б "TJQKA"і ні "cdhs"?
luser droog

Ой. Правильно. intс. Я розумію. Можливо, все-таки варто того, щоб зберегти всі розділові знаки. Може навіть профакторізовать charвідмова від getcharі putcharз божевільною пастообразной макро ...
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 і програма продовжується.

По-друге, тому що символьний потік, здається, звідкись отримує новий рядок, навіть якщо він не постачається (хороший ol 'PHP), і це невизначений індекс у масиві. Це останній символ введення даних і не впливає на вихід.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.