what is the best way to generate a cartesian product of some lists, not knowing in advance how many lists there are?
如果您愿意,可以在这里停止阅读。
我没有钱上学所以我正在努力教自己一些编程 在高速公路收费站工作夜班时使用互联网。我有 决定尝试解决一些“编程挑战”问题作为练习。
这是我要解决的问题,TopCoder的属性:
http://community.topcoder.com/stat?c=problem_statement&pm=3496
我不会复制并粘贴完整的说明以尊重他们的版权声明 但我假设我可以总结一下,前提是我不要逐字逐句地使用它 (IANAL虽然)。
如果历史股票价格的“加权金额”是获得的附录的总和 通过将这些价格的子集乘以相等数量的“权重” 因素,只要后者加起来 1.0 并从给定的集合中选择 有效值 [ - 1.0,-0.9,...,0.9,1.0] ,在所有上使用此公式 作为函数参数提供的历史数据,检查 5 价格 一次,预测下一个价格并返回“加权”的排列 因素“产生最低的平均预测误差。至少会有 每次运行6个股票价格,因此至少保证一个预测,最终 结果应该在1E-9之内准确。
格式:
list
从下载:
import itertools
# For a permutation of factors to be used in a weighted sum, it should be chosen
# such than the sum of all factors is 1.
WEIGHTED_SUM_TOTAL = 1.0
FACTORS_CAN_BE_USED_IN_WEIGHTED_SUM = lambda x: sum(x) == WEIGHTED_SUM_TOTAL
# Historical stock price data should be examined using a sliding window of width
# 5 when making predictions about the next price.
N_RECENT_PRICES = 5
# Valid values for weighting factors are: [-1.0, -0.9, ..., 0.9, 1.0]
VALID_WEIGHTS = [x / 10. for x in range(-10, 11)]
# A pre-calculated list of valid weightings to consider. This is the cartesiant
# product of the set of valid weigths considering only the combinations which
# are valid as components of a weighted sum.
CARTESIAN_PRODUCT_FACTORS = [VALID_WEIGHTS] * N_RECENT_PRICES
ALL_PERMUTATIONS_OF_WEIGHTS = itertools.product(*CARTESIAN_PRODUCT_FACTORS)
WEIGHTED_SUM_WEIGHTS = filter(FACTORS_CAN_BE_USED_IN_WEIGHTED_SUM,
ALL_PERMUTATIONS_OF_WEIGHTS)
# Generator function to get sliding windows of a given width from a data set
def sliding_windows(data, window_width):
for i in range(len(data) - window_width):
yield data[i:i + window_width], data[i + window_width]
def avg_error(data):
# The supplied data will guarantee at least one iteration
n_iterations = len(data) - 5
best_average_error = None
# Consider each valid weighting (e.g. permutation of weights)
for weighting in WEIGHTED_SUM_WEIGHTS:
# Keep track of the prediction errors for this weighting
errors_for_this_weighting = []
for historical_data, next_to_predict in sliding_windows(data,
N_RECENT_PRICES):
prediction = sum([a * b for a, b in zip(weighting, historical_data)])
errors_for_this_weighting.append(abs(next_to_predict - prediction))
average_error = sum(errors_for_this_weighting) / n_iterations
if average_error == 0: return average_error
best_average_error = (average_error if not best_average_error else
min(average_error, best_average_error))
return best_average_error
def main():
with open('data.txt') as input_file:
while True:
data = eval(input_file.readline())
expected_result = eval(input_file.readline())
spacer = input_file.readline()
if not spacer:
break
result = avg_error(data)
print expected_result, result, (expected_result - result) < 1e-9
if __name__ == '__main__':
main()
我不是要求对我的解决方案进行代码审查,因为这将是错误的StackExchange论坛。在这种情况下,我会将解决方案发布到“代码审查”。
我的问题是小而精确且毫不含糊,符合本网站的格式(希望如此)。
在我的代码中,我使用itertools生成列表的笛卡尔积。从本质上讲,我自己并没有解决问题的关键,而是将解决方案委托给一个为我这样做的库。如果我想通过这些练习学习,我认为这是错误的方法。我自己应该做的很难,否则为什么要做这个练习呢?所以我想问你:
what is the best way to generate a cartesian product of some lists, not knowing in advance how many lists there are?
这就是我想知道的,如果你愿意,你可以批评我的代码。这是值得欢迎的,即使它通过了所有的测试(总是有一种更好的做事方式,特别是如果你是像我一样的初学者)但是对于这个问题来说“对你来说是正确的”,我只关注一个方面代码,我遇到的具体问题以及我不满意的事情。让我告诉你更多,我也会分享规范的“你已经尝试过的东西”......
显然,如果我知道列表的数量,我可以输入一些嵌套的for循环,就像本次练习的顶级解算器在比赛中所做的那样。我尝试编写一个函数,为未知数量的列表执行此操作,但我不确定采用哪种方法。第一种方法是编写递归函数。从列表1中,取出元素1并将其与列表2的元素1,列表3的元素1等组合。我将从每个“层”的元素推入堆栈并在达到所需深度时弹出它们。我想我不会害怕“堆栈溢出”,因为深度可达是合理的。然后,我努力选择一种数据结构,以尽可能最有效(内存/空间)的方式完成此操作,而不会向递归调用传递太多参数。数据结构是否应该存在于调用之外?在电话中传递?我能达到任何级别的并行度吗?怎么样?有这么多的问题和很少的答案,我意识到我需要知道更多来解决这个问题,我可以使用正确的方向轻推。你可以提供一个代码片段,我会研究它。或者只是向我解释一下处理这类问题的正确“计算机科学”方法是什么。我确信有一些我不在考虑的事情。
最后,我在我上面的解决方案中所考虑的事情是,谢天谢地过滤器过滤器生成器,因此完整的笛卡尔产品永远不会保存在内存中(就像我做了一个列表一样(ALL_PERMUTATIONS_OF_WEIGHTS) )在代码中的任何时间)所以我在内存中占用空间仅用于那些实际上可以用作加权和的组合。如果应用于任何允许我使用itertools生成笛卡尔积而不使用的系统,那么类似的警告会很好。
答案 0 :(得分:4)
考虑如何编写数字(在十进制系统中,或在任何其他系统中)。即使您不需要,也包括零:
00000
00001
00002
...
00009
00010
00011
00012
...
99998
99999
您可以看到这看起来像5个列表list(range(10))
的笛卡尔积(在这种特殊情况下)。您可以通过递增“最低”数字来非常轻松地生成此输出,当它到达列表中的最后一个数字时,将其设置为第一个元素并递增“下一个最高”数字。当然,你仍然需要for
个循环,但数量非常少。使用任意数量的任意列表时,请使用类似的方法。
例如,如果您有3个列表:['a', 'b', 'c']
,['x', 'y']
,['1', '2']
,您将获得:
ax1
ax2
ay1
ay2
bx1
bx2
by1
by2
cy1
cy2
cx1
cx2
祝你好运!
编辑:
如果您愿意,可以使用以下示例代码。我没有递归只是为了表明这是多么简单。当然,递归也是一种很好的方法。
def lex_gen(bounds):
elem = [0] * len(bounds)
while True:
yield elem
i = 0
while elem[i] == bounds[i] - 1:
elem[i] = 0
i += 1
if i == len(bounds):
raise StopIteration
elem[i] += 1
def cart_product(lists):
bounds = [len(lst) for lst in lists]
for elem in lex_gen(bounds):
yield [lists[i][elem[i]] for i in range(len(lists))]
for k in cart_product([['1', '2'], ['x', 'y'], ['a', 'b', 'c']]):
print(k)
答案 1 :(得分:3)
首先,考虑一个n-list笛卡尔积。让我们取第一个列表,我们将其称为L.然后我们将采用剩余的列表,我们将其称为R.然后,对于L中的每个项目,前置这是由R的笛卡尔积产生的每个元组的开头。
有了它,你可以通过实现无列表的笛卡尔积来解决问题。
这是一个Haskell实现,以防它帮助您理解我所说的内容:
cartesian :: [[a]] -> [[a]]
cartesian [] = [[]]
cartesian (xs:yss) = [x : ys | x <- xs, ys <- cartesian yss]
答案 2 :(得分:1)
这是我最喜欢的(以及教学上体面的,我希望的)实现笛卡尔积的方法reduce
,翻译自我前段时间写的Perl version:
def cartesian_product(*X):
return reduce(
lambda accum, list:
[ tup + (item,) for tup in accum for item in list ],
X,
[()]
)
它类似于hayden的答案,除了它使用reduce
而不是显式递归,我认为这使基本情况更加清晰。
我们在这里减少的是一个元组列表(累计输出,accum
)对项目列表(list
)。对于项目列表中的每个项目,我们将其连接到所有累积元组的末尾,并为尽可能多的列表(X
)重复此过程。 reduce初始值设定项为[()]
,这是一个包含一个空元组的列表,可确保X[0]
为[1, 2, 3]
时,累加器将在第一步后变为[(1), (2), (3)]
(一个元组,因为我们希望X[0]
中的每个项目,以及零元组,因为我们希望它连接到 nothing )。这对应于发送者在对icktoofay的回答的评论中提到的“nullary产品”。
鉴于此功能定义,如果您print cartesian_product([1,2], [3,4], [5,6])
将打印:
[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
这是我们预期的8个元组。
答案 3 :(得分:0)
经典地,笛卡尔坐标在平面中为(x,y)
或在3D空间中为(x,y,z)
(对于实数中的x,y和z):
[ (x,y) for x in reals for y in reals ]
更一般地说,它们是元组(作为Python列表理解):
[ (x1, x2, x3, ...) for x1 in X1 for x2 in X2 for x3 in X3 ...]
对于对象(在我们的例子中是迭代)X1, X2, X3,...
,我们希望是一个函数:
def cartesian_product(X1,X2,X3,...):
return # the above list
执行此操作的一种方法是使用递归,注意始终返回元组:
def cartesian_product(*X):
if len(X) == 1: #special case, only X1
return [ (x0,) for x0 in X[0] ]
else:
return [ (x0,)+t1 for x0 in X[0] for t1 in cartesian_product(*X[1:]) ]
cartesian_product([1,2],[3,4],[5,6])
# [(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
答案 4 :(得分:0)
Itertools救援。以下将逐个创建组合:
import itertools
combs=itertools.product(*lists)
电子。 G。使用命令行Python,并假设您有一个可变长度列表列表:
>>> c=[['3', '5', '7'], ['100'], ['1', '2', '3']]
>>> z=itertools.product(*c)
>>> for ii in z:
... print ii
...
('3', '100', '1')
('3', '100', '2')
('3', '100', '3')
('5', '100', '1')
('5', '100', '2')
('5', '100', '3')
('7', '100', '1')
('7', '100', '2')
('7', '100', '3')