Python中正面和反面在n次投掷中的排列列表,正面和反面在k次投掷中的排列

Python中正面和反面在n次投掷中的排列列表,正面和反面在k次投掷中的排列,python,combinations,permutation,probability,combinatorics,Python,Combinations,Permutation,Probability,Combinatorics,我正试图编写一些python代码,列出给定n次抛投和正好k次抛投的正面和反面的不同排列。例如,对于n=6次投掷和k=3个头,我想要的输出是['000111','001011','001101','001110','010011','010101','010110','011001','011001','100101','100110','101001','101010','101100','110001','110010','110100','111000'] 注:这不是关于计算概率的问题,没有

我正试图编写一些python代码,列出给定n次抛投和正好k次抛投的正面和反面的不同排列。例如,对于n=6次投掷和k=3个头,我想要的输出是['000111','001011','001101','001110','010011','010101','010110','011001','011001','100101','100110','101001','101010','101100','110001','110010','110100','111000'] 注:这不是关于计算概率的问题,没有问题。我的问题是为n次抛投和k次头部生成所有不同的排列

我知道最终字符串的数量由二项系数aka组合aka n_choose_k给出,例如scipy.special.comb。但是,我很难为超过10次的投掷次数生成这些不同的选项。我的第一个简单方法是使用itertools.permutations生成字符串的所有可能排列,例如11111 00000,然后使用set删除重复项。这适用于低n n10,研磨停止。我知道itertools也有组合生成器和_以及_替换。但是,我不确定是否可以使用它以我需要的方式生成输出,因为我的问题不是从长度n的集合中选择k个元素的子集,其中顺序并不重要

我的初始代码如下

from itertools import permutations
import scipy.special

n = 10
k = n//2
n_choose_k = scipy.special.comb(n, k)
print('{} choose {} = {}'.format(n, k, n_choose_k))

U,D = '1','0'
seed = U * k + D * (n-k)
permlist = [''.join(p) for p in permutations(seed)]
permset = sorted(list(set(permlist)))
print(permset)
print('len permutations:{}, len set:{}'.format(len(permlist), len(permset)))
注意:我对n次投掷和k个头部很感兴趣,尽管我很好奇如何将解决方案扩展到至少k个头部

更新: 我已经接受了。但对于那些好奇的人来说,这里有三种在i7-9750H@2.6Ghz笔记本电脑上运行的方法

结果: 代码:
这不是一个公平的解决方案,但您可以计算2**k-1并以二进制格式写入0和2**k-1之间的所有整数。此列表将包含所有0和1的组合,总共有k个数字。

这不是一个公平的解决方案,但您可以计算2**k-1并以二进制格式写入0和2**k-1之间的所有整数。此列表将包含所有0和1的组合,总共有k个数字。

将整个排列列表显示为字符串是一个巨大的内存消耗。您可以使用@aramakus的一个修改版本,通过使用整数和一个使用位移位的检查器来生成只有k个整数的整数,从而将什么是可行的边界推到一位:

输出:

000000001111111
000000010111111
000000011011111
000000011101111
000000011110111
...
111111001000000
111111010000000
111111100000000
exactly 7 ones: 6435 out of 32768 numbers
使用time.time进行大致计算的时间,因此不会重复/平均多次运行的计时,最可靠的是,会涉及一些误差度量:

 # You should do this using timeit.timeit() for serious measurements

 n   k   time in seconds
 5   2   0.0019998550415039062
 6   3   0.004002809524536133
 7   3   0.009006261825561523
 8   4   0.020014286041259766
 9   4   0.04403090476989746
10   5   0.09506702423095703
11   5   0.20834732055664062
12   6   0.4523327350616455
13   6   0.9736926555633545
14   7   2.0954811573028564
15   7   4.479296922683716
16   8   9.40683913230896
17   8   19.881306886672974
18   9   41.978920459747314

# somewhat "linear" calculation time

将整个排列列表显示为字符串是一个巨大的内存消耗。您可以使用@aramakus的一个修改版本,通过使用整数和一个使用位移位的检查器来生成只有k个整数的整数,从而将什么是可行的边界推到一位:

输出:

000000001111111
000000010111111
000000011011111
000000011101111
000000011110111
...
111111001000000
111111010000000
111111100000000
exactly 7 ones: 6435 out of 32768 numbers
使用time.time进行大致计算的时间,因此不会重复/平均多次运行的计时,最可靠的是,会涉及一些误差度量:

 # You should do this using timeit.timeit() for serious measurements

 n   k   time in seconds
 5   2   0.0019998550415039062
 6   3   0.004002809524536133
 7   3   0.009006261825561523
 8   4   0.020014286041259766
 9   4   0.04403090476989746
10   5   0.09506702423095703
11   5   0.20834732055664062
12   6   0.4523327350616455
13   6   0.9736926555633545
14   7   2.0954811573028564
15   7   4.479296922683716
16   8   9.40683913230896
17   8   19.881306886672974
18   9   41.978920459747314

# somewhat "linear" calculation time
但是,我不确定是否可以使用它以我需要的方式生成输出,因为我的问题不是从长度n的集合中选择k个元素的子集,其中顺序并不重要

实际上,这是一种横向思维。要从中选择的集合是一组索引,其中1应出现在给定的输出中

因此:使用itertools.compositions来确定1应该去哪里的指数,我们从n个可能的指数值中选择k值-0到n-1,包括在内-无需替换;这正是组合的含义,然后为每个组合生成字符串。例如,作为生成器:

def bit_strings(size, one_count):
    for one_indices in itertools.combinations(range(size), one_count):
        yield ''.join('1' if i in one_indices else '0' for i in range(size))

>>> len(list(bit_strings(20, 10))) # takes a bit less than a second on my machine
184756
当然,这仍然是指数级的!比直接计算组合数慢

但是,我不确定是否可以使用它以我需要的方式生成输出,因为我的问题不是从长度n的集合中选择k个元素的子集,其中顺序并不重要

实际上,这是一种横向思维。要从中选择的集合是一组索引,其中1应出现在给定的输出中

因此:使用itertools.compositions来确定1应该去哪里的指数,我们从n个可能的指数值中选择k值-0到n-1,包括在内-无需替换;这正是组合的含义,然后为每个组合生成字符串。例如,作为生成器:

def bit_strings(size, one_count):
    for one_indices in itertools.combinations(range(size), one_count):
        yield ''.join('1' if i in one_indices else '0' for i in range(size))

>>> len(list(bit_strings(20, 10))) # takes a bit less than a second on my machine
184756

当然,这仍然是指数级的!比直接计算组合数慢。

二进制方法是一种很好的方法,不能推广到其他情况,但可能解决我的特定问题。但如果我理解正确的话,这似乎是完全不同的。首先,n在这方面有什么特点?还有一个约束条件,就是必须有k1?啊,我想我误解了你的解决方案,我一直在想它
离子与k投掷。您只计算到2**k-1这一事实确保了只有k个数字。我同意,这是一个针对某个特定问题的可爱把戏。对于n 1s和每位数的加法求和,只要和=n,就需要进行检查。是的,这是一种有趣的方法。实际上,作为一个额外的优化,可以从2**k-1开始,因为它被保证是k个1的最小允许整数。因此,它变成了检查2**k-1和2**n-1之间所有整数的情况。我不确定这会有多高效,但出于好奇,我会尝试一下,谢谢。是的,我认为从优化的角度来看,这不是很理想。它本质上是一种蛮力检查,但生成速度将相当快,因为十进制到二进制的转换非常简单。二进制方法是一种很好的方法,不能推广到其他情况,但可能解决我的特定问题。但如果我理解正确的话,这似乎是完全不同的。首先,n在这方面有什么特点?还有一个约束条件,那就是必须有k1?啊,我想我误解了你的解决方案,我认为它与k抛的组合。您只计算到2**k-1这一事实确保了只有k个数字。我同意,这是一个针对某个特定问题的可爱把戏。对于n 1s和每位数的加法求和,只要和=n,就需要进行检查。是的,这是一种有趣的方法。实际上,作为一个额外的优化,可以从2**k-1开始,因为它被保证是k个1的最小允许整数。因此,它变成了检查2**k-1和2**n-1之间所有整数的情况。我不确定这会有多高效,但出于好奇,我会尝试一下,谢谢。是的,我认为从优化的角度来看,这不是很理想。这本质上是一个蛮力检查,但生成速度将相当快,因为十进制到二进制的转换非常简单。谢谢,是的,这似乎是可行的。作为一种优化,范围可以从2**k-1开始,因为这保证是最小的int和k 1s。n=20后,它开始变得非常慢:我还没等它完成呢@关于起始限制,请记住一个好主意——虽然只有少数测试用例被排除在外,但这是一个非最小的优化,是的,这似乎是可行的。作为一种优化,范围可以从2**k-1开始,因为这保证是最小的int和k 1s。n=20后,它开始变得非常慢:我还没等它完成呢@请注意起始限制的好主意-虽然只有少数测试用例被排除在外,但这是一个非lessneat编辑的优化:也可以在自我回答中列出,已经对你的问题进行了升级编辑:也可以在自我回答中列出,已经对你的问题进行了升级