我一直以为如果我像这样使用for循环:
for line in lines:
dosomething()
我永远不会让我的列表索引超出范围。但是,我错了..
我的代码的目的是输出输入集的所有子集。
示例:
输入:
[2,1,3]
输出:
[[],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]]
以下是代码:
def bin_list(bit_len, list_len):
# To get a binary numbers list like this:
# ['000', '001', '010', '011', '100', '101', '110', '111']
list_Bin = [None] * list_len
for i in range(list_len):
extra_d = bit_len-len(bin(i)[2:])
list_Bin[i] = '0'*extra_d + bin(i)[2:]
return list_Bin
def subsets(nums):
nums.sort()
max_element = len(nums)
size_subset = 2 ** max_element
list_Bin = bin_list(max_element, size_subset)
list_Result = [[None]*max_element for i in range(size_subset)]
for i, Bin in enumerate(list_Bin):
for j in range(max_element):
if Bin[j] == '1':
#1 list_Result[i][j] = nums[i]
#2 list_Result[i].append(nums[i])
print(list_Result)
nums = [1,2,3]
subsets(nums)
所以我的核心概念是使用二进制列表来创建子集列表。
例如,当我在二进制列表中读取001时。我会
由于第三位为1 ,因此,如果我使用0-7(二进制== 000-111),我可以得到[1,2,3]的所有子集。
但我总是得到IndexError: list index out of range
。无论我使用
#1 list_Result[i][j] = nums[i]
或
#2 list_Result[i].append(nums[i])
答案 0 :(得分:3)
由于以下原因,您收到错误消息:
len(nums) == 3
size_subset == 2**3 == 8
nums[3]
从i
转到0
的循环中访问len[list_Bin]
。解决方案可能是访问nums[j]
而不是nums[i]
,因为j
从0
转到len(nums)
。
答案 1 :(得分:3)
IndexError
的原因是你的指数混乱了。在for
函数subsets
中的嵌套i
循环中,子集数量范围超过j
范围内nums
范围内的nums[i]
范围。但是,当 正在执行nums[j]
时,您尝试访问i
,因此当len(nums)
大于或等于nums
时,您将尝试要访问IndexError
列表的末尾,因此bin
。
您的代码可以简化一些。没有必要使用format
函数:你可以告诉def bin_list(bit_len):
list_len = 2 ** bit_len
return ['{0:0{1}b}'.format(i, bit_len) for i in range(list_len)]
def subsets(seq):
all_subsets = []
for bits in bin_list(len(seq)):
s = [seq[i] for i, b in enumerate(bits) if b == '1']
all_subsets.append(s)
return all_subsets
print(subsets([1,2,3]))
print(subsets('ABCD'))
格式化二进制的整数,并保持所需的长度。
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
[[], ['D'], ['C'], ['C', 'D'], ['B'], ['B', 'D'], ['B', 'C'], ['B', 'C', 'D'], ['A'], ['A', 'D'], ['A', 'C'], ['A', 'C', 'D'], ['A', 'B'], ['A', 'B', 'D'], ['A', 'B', 'C'], ['A', 'B', 'C', 'D']]
<强>输出强>
'{0:0{1}b}'.format(i, bit_len)
我承认'{0:0{1}b}'.format(i, bit_len)
有点神秘,所以我会尝试尽可能简单地解释它,从Format String Syntax的官方Python 3文档中自由引用。
表达式i
创建一个包含bit_len
二进制表示的字符串。创建的字符串宽'{0:0{1}b}'
个字符,如果需要,在左边填充零。
{}
部分称为格式字符串。格式字符串包含由大括号{
包围的“替换字段”。这是一个复杂的例子,因为它有一个嵌套在另一个替换字段中的替换字段。
在替换字段中,紧跟.format
之后的项称为field_name。它用于指定.format
的哪个参数与此替换字段相关联。 field_name可以是参数的名称,它的位置,或者它可以省略(在Python 2.7及更高版本中),Python只会在数字顺序中使用!
参数,当它们与替换字段匹配时
field_name可选地后跟一个转换字段,前面有一个感叹号:
,还有一个format_spec(格式规范),前面有一个冒号b
。这些指定替换值的非默认格式。此特定格式字符串没有转换字段,因此使用默认转换。
format_spec以字母结尾,指定我们想要的格式类型。我们的format_spec以b
结尾,表示我们要将整数格式化为二进制。紧接在08b
之前的数字指定了结果位串应该有多宽;通过在前面加零,我们说我们希望用零填充字符串而不是空格。因此,bit_len
的format_spec将为我们提供8个字符宽的位字符串(如果需要,可以更宽地正确表示参数),短位字符串用零填充。
但我们需要更高级的东西,因为我们没有固定的位字符串宽度 - 我们希望我们的位字符串宽08b
个字符。但那没关系,因为格式语法允许我们将替换字段放在另一个替换字段中!所以我们不是0{1}b
而是'{val:0{width}b}'.format(val=i, width=bit_len)
,现在位置1中的参数值被用作位字符串宽度。
我之前提到过field_name可以是参数的名称,因此我们的原始格式表达式也可以写成:
'{:0{}b}'.format(i, bit_len)
我猜这种形式比原始形式更具可读性。 :)
我还提到可以省略field_name(在Python 2.7及更高版本中),如下所示:
bin_list
但我不建议这样做,因为虽然它更短,但它比原版恕我直言更加神秘。
但是,我们并不需要def subsets(seq):
bit_len = len(seq)
bitrange = range(bit_len)
all_subsets = []
for j in range(1 << bit_len):
s = [seq[i] for i in bitrange if j & (1<<i)]
all_subsets.append(s)
return all_subsets
print(subsets([1,2,3]))
print(subsets('ABCD'))
中的位字符串:我们可以在子集索引上使用按位运算符。
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
[[], ['A'], ['B'], ['A', 'B'], ['C'], ['A', 'C'], ['B', 'C'], ['A', 'B', 'C'], ['D'], ['A', 'D'], ['B', 'D'], ['A', 'B', 'D'], ['C', 'D'], ['A', 'C', 'D'], ['B', 'C', 'D'], ['A', 'B', 'C', 'D']]
<强>输出强>
def subsets(seq):
bit_len = len(seq)
bitrange = range(bit_len)
return [[seq[i] for i in bitrange if j & (1<<i)]
for j in range(1 << bit_len)]
这确实以不同的顺序产生子集,但是改变它并不难,我认为这个顺序更自然。
我们可以通过使用嵌套列表理解来使最后一个版本更紧凑:
def subsets(seq):
z = [[]]
for x in seq:
z += [y + [x] for y in z]
return z
FWIW,这是另一种制作子集的方法。它的效率有点低,但对于小输入序列来说却没问题。它以与先前版本相同的顺序生成子集。
max-width: certain-px-smaller-than-content;