我需要创建一个函数,给定一个正整数列表(可能有重复的整数),计算所有三元组(在列表中),其中第三个数字是第二个的倍数,第二个是第一个的倍数:
(相同的数字不能在一个三元组中使用两次,但可以被所有其他三元组使用)
例如,[3, 6, 18]
是因为18
均匀地进入6
其中3
.
所以鉴于[1, 2, 3, 4, 5, 6]
它应该找到:
[1, 2, 4] [1, 2, 6] [1, 3, 6]
Run Code Online (Sandbox Code Playgroud)
并返回3
(它找到的三元组数)
我做了一些有效的功能,但效率不高.是否有一些我不知道的数学概念可以帮助我更快地找到这些三元组?具有更好功能的模块?我不知道该搜索什么...
def foo(q):
l = sorted(q)
ln = range(len(l))
for x in ln:
if len(l[x:]) > 1:
for y in ln[x + 1:]:
if (len(l[y:]) > 0) and (l[y] % l[x] == 0):
for z in ln[y + 1:]:
if l[z] % l[y] == 0:
ans += 1
return ans
Run Code Online (Sandbox Code Playgroud)
这个更快一点:
def bar(q):
l = sorted(q)
ans = 0
for x2, x in enumerate(l):
pool = l[x2 + 1:]
if len(pool) > 1:
for y2, y in enumerate(pool):
pool2 = pool[y2 + 1:]
if pool2 and (y % x == 0):
for z in pool2:
if z % y == 0:
ans += 1
return ans
Run Code Online (Sandbox Code Playgroud)
这是我从你们所有人那里得到的帮助,但我必须做错事,因为它得到了错误的答案(尽管它真的很快):
def function4(numbers):
ans = 0
num_dict = {}
index = 0
for x in numbers:
index += 1
num_dict[x] = [y for y in numbers[index:] if y % x == 0]
for x in numbers:
for y in num_dict[x]:
for z in num_dict[y]:
print(x, y, z)
ans += 1
return ans
Run Code Online (Sandbox Code Playgroud)
(39889
而不是40888
) - 哦,我不小心使索引var从1开始而不是0.它现在有效.
通过重新评估我需要它做什么,我找到了找到三元组数量的最佳方法.这种方法实际上并没有找到三元组,它只计算它们.
def foo(l):
llen = len(l)
total = 0
cache = {}
for i in range(llen):
cache[i] = 0
for x in range(llen):
for y in range(x + 1, llen):
if l[y] % l[x] == 0:
cache[y] += 1
total += cache[x]
return total
Run Code Online (Sandbox Code Playgroud)
这里有一个函数版本可以解释思考过程(虽然因为垃圾邮件打印而对大型列表不利):
def bar(l):
list_length = len(l)
total_triples = 0
cache = {}
for i in range(list_length):
cache[i] = 0
for x in range(list_length):
print("\n\nfor index[{}]: {}".format(x, l[x]))
for y in range(x + 1, list_length):
print("\n\ttry index[{}]: {}".format(y, l[y]))
if l[y] % l[x] == 0:
print("\n\t\t{} can be evenly diveded by {}".format(l[y], l[x]))
cache[y] += 1
total_triples += cache[x]
print("\t\tcache[{0}] is now {1}".format(y, cache[y]))
print("\t\tcount is now {}".format(total_triples))
print("\t\t(+{} from cache[{}])".format(cache[x], x))
else:
print("\n\t\tfalse")
print("\ntotal number of triples:", total_triples)
Run Code Online (Sandbox Code Playgroud)
现在你的算法有 O(N^3) 运行时间,这意味着每次你将初始列表的长度加倍,运行时间就会增加 8 倍。
在最坏的情况下,你无法改善这一点。例如,如果你的数字都是 2 的连续幂,这意味着每个数字都除以大于它的每个数字,那么每个三倍数都是一个有效的解决方案,因此打印出所有解决方案将与打印所有解决方案一样慢你现在正在做的。
如果除其他数字的数字“密度”较低,您可以做的加快速度的一件事是搜索数字对而不是三元组。这将只花费 O(N^2) 的时间,这意味着当输入列表的长度加倍时,运行时间会增加 4 倍。一旦你有了一个数字对列表,你就可以用它来构建一个三元组列表。
# For simplicity, I assume that a number can't occur more than once in the list.
# You will need to tweak this algorithm to be able to deal with duplicates.
# this dictionary will map each number `n` to the list of other numbers
# that appear on the list that are multiples of `n`.
multiples = {}
for n in numbers:
multiples[n] = []
# Going through each combination takes time O(N^2)
for x in numbers:
for y in numbers:
if x != y and y % x == 0:
multiples[x].append(y)
# The speed on this last step will depend on how many numbers
# are multiples of other numbers. In the worst case this will
# be just as slow as your current algoritm. In the fastest case
# (when no numbers divide other numbers) then it will be just a
# O(N) scan for the outermost loop.
for x in numbers:
for y in multiples[x]:
for z in multiples[y]:
print(x,y,z)
Run Code Online (Sandbox Code Playgroud)
可能有更快的算法,它也利用除法的代数属性,但在你的情况下,我认为 O(N^2) 可能足够快。