使用随机输入输出混洗的牌组


9

输入输出:

输入:取自stdin的均匀随机,无限长的字符串'0'和'1'。假定该字符串是真正随机的,而不是伪随机的。这是统一的,因为每个字符均可能是“ 0”或“ 1”。

小心!输入是无限长的,因此您无法使用python中的raw_input()之类的函数将其全部存储在内存中。如果我没记错的话,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

Answers:


7

Ruby,共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

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)

说明:

未使用的卡存储在D中。这只是从输入流中获取下一个有效的随机索引,并从D中弹出该元素。

除非我缺少任何东西,否则不应有偏见。该脚本将抛出任何无效的索引> len(D),但这不会导致数字偏低,因为每个连续的弹出都会使每个元素的索引比i减少。


因此,您丢弃了大多数(无限)随机输入?也就是说,一旦您没有“未使用”的卡片,您就停止洗牌了吗?
Leigh 2012年

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,从而导致字母顺序偏向。
12

你说得对,@ boothby。如果多张卡具有相同的“权重”,则排序确实会使该解决方案带有偏见。也不能保证此解决方案会产生结果。我将对它的工作方式进行描述,以便比我聪明的人可以分析它
ardnew 2012年

只要某些输入导致程序永不终止就可以了,只要程序终止的概率在时间接近无穷大时就接近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位数十进制数%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初始化一个52个整数的数组,随机播放(将元素x随机替换为0..x范围内的另一个元素),打印格式为(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
霍华德

t输出时不必仍然放回阵列中
l4m2,18年

3

Unix外壳〜350

这不是简短或漂亮,也不是有效的,但是我想知道使用标准的unix shell实用程序很难做到这一点。

该答案将无限二进制字符串切成6位长度,并且仅选择正确范围内的长度(1-52),这里无限二进制字符串由urandom和xxd模拟:

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

通过fold,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,但是大多数现代的shell都应该适用于此):

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轻松生成:

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

将最后两个命令的输出与paste结合并按数字排序:

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中使用的版本。我删除了in-shell { },数组索引从零开始。数组空度通过参数扩展进行检查,效率更高一点,并且在上面的示例中也采用了这种方法。

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我有很多东西要学。顺便说一句,为什么不"TJQKA""cdhs"
luser droog 2012年

哦。对。ints。我知道了。保存所有标点符号仍然值得。甚至可能因素的char出来getchar,并putchar用一个疯狂的糊状宏观...
luser楚格设计

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很好),并且这是数组中的未定义索引。它是输入的最后一个字符,不会影响输出。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.