给定子列表数量,展示列表的所有可能分组
问题
第一步: 给定一串数字,生成所有可能的分组(按顺序),只根据想要的最终组数来进行分组。
举个例子,如果我的数字是1到4,而我想要2个最终的组,那么可能的分组方式有:
[1], [2,3,4]
[1,2], [3,4]
[1,2,3], [4]
第二步: 对这些组进行算术运算。
比如,如果我们选择加法,最后的结果会是:
1 + 234 = 235
12 + 34 = 46
123 + 4 = 127
之前的研究和类似问题
我在StackOverflow和其他地方看到过很多关于组数可变的问题,这些问题通常使用范围和循环,比如:
print [num_list[i:i+groups] for i in range(0,len(num_list),groups)]
但这其实和我想要的正好相反——在那些例子中,组的长度是固定的,除了最后一个,而组的数量是变化的。
这不是作业,只是我遇到的一个有趣问题。理想情况下,我需要能够遍历这些子列表,以便进行数学运算,所以这些子列表也需要被捕获。
我觉得解决方案可能会涉及到itertools,但我似乎搞不清楚如何处理分组的组合问题。
第二步的编辑/扩展
如果我想对每个分组执行不同的操作,我还可以用同样的方法吗?而不是仅仅指定int.add,我能否以某种方式对所有主要的四种运算进行另一种组合?也就是说:
symbol_list = ['+','-','*','/']
for op in symbol_list:
#something
我最终会得到以下可能性:
1 + 2 * 34
1 * 2 - 34
1 / 2 + 34
etc.
运算的顺序可以被忽略。
最终解决方案
#!/usr/bin/env python
import sys
from itertools import combinations, chain, product
# fixed vars
num_list = range(_,_) # the initial list
groups = _ # number of groups
target = _ # any target desired
op_dict = {'+': int.__add__, '-': int.__sub__,
'*': int.__mul__, '/': int.__div__}
def op_iter_reduce(ops, values):
op_iter = lambda a, (i, b): op_dict[ops[i]](a, b)
return reduce(op_iter, enumerate(values[1:]), values[0])
def split_list(data, n):
for splits in combinations(range(1, len(data)), n-1):
result = []
prev = None
for split in chain(splits, [None]):
result.append(data[prev:split])
prev = split
yield result
def list_to_int(data):
result = 0
for h, v in enumerate(reversed(data)):
result += 10**h * v
return result
def group_and_map(data, num_groups):
template = ['']*(num_groups*2 - 1) + ['=', '']
for groups in split_list(data, num_groups):
ints = map(list_to_int, groups)
template[:-2:2] = map(str, ints)
for ops in product('+-*/', repeat=num_groups-1):
template[1:-2:2] = ops
template[-1] = str(op_iter_reduce(ops, ints))
if op_iter_reduce(ops, ints) == target:
print ' '.join(template)
group_and_map(num_list, groups)
3 个回答
第一步:
我尝试了所有可能的索引组合:
from itertools import combinations
def cut(lst, indexes):
last = 0
for i in indexes:
yield lst[last:i]
last = i
yield lst[last:]
def generate(lst, n):
for indexes in combinations(list(range(1,len(lst))), n - 1):
yield list(cut(lst, indexes))
举个例子:
for g in generate([1, 2, 3, 4, 5], 3):
print(g)
"""
[[1], [2], [3, 4, 5]]
[[1], [2, 3], [4, 5]]
[[1], [2, 3, 4], [5]]
[[1, 2], [3], [4, 5]]
[[1, 2], [3, 4], [5]]
[[1, 2, 3], [4], [5]]
"""
第二步:
首先,我们需要把一组数字转换成数字:
for g in generate(list(range(1,6)), 3):
print([int(''.join(str(n) for n in n_lst)) for n_lst in g])
"""
[1, 2, 345]
[1, 23, 45]
[1, 234, 5]
[12, 3, 45]
[12, 34, 5]
[123, 4, 5]
"""
然后使用 reduce
和 operator
来进行算术运算:
(虽然这最后一步其实和你的问题没太大关系)
from functools import reduce
import operator
op = operator.mul
for g in generate(list(range(1,6)), 3):
converted = [int(''.join(str(n) for n in n_lst)) for n_lst in g]
print(reduce(op, converted))
"""
690
1035
1170
1620
2040
2460
"""
Raymond Hettinger 写了一个方法,可以用来把一个可迭代的对象分成 n
组:
import itertools
import operator
def partition_indices(length, groups, chain = itertools.chain):
first, middle, last = [0], range(1, length), [length]
for div in itertools.combinations(middle, groups-1):
yield tuple(itertools.izip(chain(first, div), chain(div, last)))
def partition_into_n_groups(iterable, groups, chain = itertools.chain):
# http://code.activestate.com/recipes/576795/
# author: Raymond Hettinger
# In [1]: list(partition_into_n_groups('abcd',2))
# Out[1]: [('a', 'bcd'), ('ab', 'cd'), ('abc', 'd')]
s = iterable if hasattr(iterable, '__getitem__') else tuple(iterable)
for indices in partition_indices(len(s), groups, chain):
yield tuple(s[slice(*x)] for x in indices)
def equations(iterable, groups):
operators = (operator.add, operator.sub, operator.mul, operator.truediv)
strfop = dict(zip(operators,'+-*/'))
for partition in partition_into_n_groups(iterable, groups):
nums_list = [int(''.join(map(str,item))) for item in partition]
op_groups = itertools.product(operators, repeat = groups-1)
for op_group in op_groups:
nums = iter(nums_list)
result = next(nums)
expr = [result]
for op in op_group:
num = next(nums)
result = op(result, num)
expr.extend((op, num))
expr = ' '.join(strfop.get(item,str(item)) for item in expr)
yield '{e} = {r}'.format(e = expr, r = result)
for eq in equations(range(1,5), groups = 2):
print(eq)
会产生
1 + 234 = 235
1 - 234 = -233
1 * 234 = 234
1 / 234 = 0.0042735042735
12 + 34 = 46
12 - 34 = -22
12 * 34 = 408
12 / 34 = 0.352941176471
123 + 4 = 127
123 - 4 = 119
123 * 4 = 492
123 / 4 = 30.75
第一步: 我发现将列表分成小组的最简单方法是尝试找出分割位置的组合。下面是一个实现的例子:
def split_list(data, n):
from itertools import combinations, chain
for splits in combinations(range(1, len(data)), n-1):
result = []
prev = None
for split in chain(splits, [None]):
result.append(data[prev:split])
prev = split
yield result
>>> list(split_list([1, 2, 3, 4], 2))
[[[1], [2, 3, 4]], [[1, 2], [3, 4]], [[1, 2, 3], [4]]]
>>> list(split_list([1, 2, 3, 4], 3))
[[[1], [2], [3, 4]], [[1], [2, 3], [4]], [[1, 2], [3], [4]]]
第二步: 首先,你需要把像 [[1], [2, 3, 4]]
这样的列表转换成 [1, 234]
这种格式。你可以用下面的函数来做到这一点:
def list_to_int(data):
result = 0
for i, v in enumerate(reversed(data)):
result += 10**i * v
return result
>>> map(list_to_int, [[1], [2, 3], [4, 5, 6]])
[1, 23, 456]
现在你可以使用 reduce()
对得到的列表进行操作:
>>> import operator
>>> reduce(operator.add, [1, 23, 456]) # or int.__add__ instead of operator.add
480
完整解决方案: 根据编辑的内容,提到需要不同的操作符:
def op_iter_reduce(ops, values):
op_dict = {'+': int.__add__, '-': int.__sub__,
'*': int.__mul__, '/': int.__div__}
op_iter = lambda a, (i, b): op_dict[ops[i]](a, b)
return reduce(op_iter, enumerate(values[1:]), values[0])
def group_and_map(data, num_groups):
from itertools import combinations_with_replacement
op_dict = {'+': int.__add__, '-': int.__sub__,
'*': int.__mul__, '/': int.__div__}
template = ['']*(num_groups*2 - 1) + ['=', '']
op_iter = lambda a, (i, b): op_dict[ops[i]](a, b)
for groups in split_list(data, num_groups):
ints = map(list_to_int, groups)
template[:-2:2] = map(str, ints)
for ops in combinations_with_replacement('+-*/', num_groups-1):
template[1:-2:2] = ops
template[-1] = str(op_iter_reduce(ops, ints))
print ' '.join(template)
>>> group_and_map([1, 2, 3, 4], 2)
1 + 234 = 235
1 - 234 = -233
1 * 234 = 234
1 / 234 = 0
12 + 34 = 46
12 - 34 = -22
12 * 34 = 408
12 / 34 = 0
123 + 4 = 127
123 - 4 = 119
123 * 4 = 492
123 / 4 = 30
如果你使用的是 Python 2.6 或更早的版本,并且 itertools.combinations_with_replacement()
不可用,你可以使用这里链接的食谱 。