python 为n个组大小为m的对象生成唯一的组合,而不重复或重复?

djp7away  于 2024-01-05  发布在  Python
关注(0)|答案(1)|浏览(138)

我正在尝试创建一个算法,该算法可以生成组大小为 nm 个对象的所有唯一组合,而无需重复或重复。
重复是指至少有两个或两个以上的数字之前已经组合在一起。例如,[1, 2, 3][1, 2, 4]具有对[1, 2]的重复。
如果不使用扩展器,则意味着所有大小为 n 的组必须大小相同。
下面的函数接受一个输入(m, n),如果 mn 不兼容,则输出False。如果 mn 兼容,则函数返回唯一组合的数量。

  1. def iterations (m, n):
  2. num = (m**2) - m
  3. den = (n**2) - n
  4. if den <= 0 or num <= 0:
  5. return False
  6. if (m - 1) % (n - 1) != 0:
  7. return False
  8. if num % den != 0:
  9. return False
  10. return int(num/den)

字符串
这部分代码工作正常。我遇到的问题是如何实现生成唯一组合的算法。
这是我用来生成唯一组合的代码块:

  1. import random
  2. class id ():
  3. def __init__(self, name):
  4. self.name = name
  5. self.comparisons = []
  6. def update_comparisons(self, id_list):
  7. for id in id_list:
  8. if id in self.comparisons:
  9. self.comparisons.remove(id)
  10. self.comparisons.extend(id_list)
  11. self.comparisons.sort()
  12. if self.name in self.comparisons:
  13. self.comparisons.remove(self.name)
  14. return self.comparisons
  15. def get_ids(n):
  16. ids = []
  17. for i in range(1,n+1):
  18. ids.append(id(i))
  19. return ids
  20. def iterations(m,n):
  21. num = (m**2) - m
  22. den = (n**2) - n
  23. if den <= 0 or num <= 0:
  24. return False
  25. if (m - 1) % (n - 1) != 0:
  26. return False
  27. if num % den != 0:
  28. return False
  29. return int(num/den)
  1. # Checking if m and n are valid values
  2. m = 9
  3. n = 3
  4. if iterations(m, n):
  5. iter = iterations(m, n)
  6. print(iter)
  1. #Creating list of ids
  2. ids_master = get_ids(m)
  3. ids = ids_master.copy()
  4. comparison_names = []
  5. ids = ids_master.copy()
  6. comparisons = []
  7. for i in range(iter):
  8. temp = []
  9. pos = 0
  10. while len(temp) < n:
  11. id_a = ids[pos]
  12. # Checking if the id within temp have already been compared or is a duplicate
  13. counter = 0
  14. for id_b in temp:
  15. if id_b.name in id_a.comparisons or id_b.name == id_a.name:
  16. counter += 1
  17. # Checking if id_a has been compared to all other ids
  18. if len(id_a.comparisons) == m - 1:
  19. counter += 1
  20. # If id_a has passed the checks, append it to temp_list
  21. if counter == 0:
  22. temp.append(id_a)
  23. pos += 1
  24. comparisons.append(temp)
  25. # Updating the comparison for each id object
  26. for comparison in comparisons:
  27. names = [x.name for x in comparison]
  28. names.sort()
  29. for id in comparison:
  30. id.update_comparisons(names)
  31. comparison_names.append(names)
  1. # Checking if all ids have been compared
  2. for id in ids_master:
  3. print(f'ID: {id.name} \n Comparisons: {id.comparisons}')

上面的代码适用于值(3,2)、(5,2)和(7,3)。然而,(9,3)带来了复杂性。
代码将在遇到路障之前生成以下比较。[[1, 2, 3], [1, 4, 5], [1, 6, 7], [1, 8, 9], [2, 4, 6], [2, 5, 7]]
在这种情况下,下一个组合将是[2, 8, 9]。这不起作用,因为对[8,9]已经在[1, 8, 9]中进行了比较。代码然后继续迭代位置,直到它用完了要检查的列表中的项目,并给出错误“list index out of range”。
我需要一种算法来预测这些错误的方法。例如,如果代码生成[2, 4, 9],那么剩余的组合 * 可能 * 会正常工作。我相信有一种方法可以实现这一点,但我不确定如何进行。
先谢谢你了!

li9yvcax

li9yvcax1#

我不能完全遵循代码中的逻辑来查看您做错了什么,但我提出了这个似乎可以满足您的要求:

  1. import itertools # for itertools.combinations
  2. import functools # for functools.reduce
  3. n=3
  4. m=9
  5. # track a list of valid combinations; a valid combination has
  6. # elements that don't have more than 1 value in common with other
  7. # valid combinations
  8. valid = []
  9. # loop through every possible combination given (m, n)
  10. for combo in itertools.combinations(range(m), n):
  11. # check each possible combination against every valid combination
  12. for v in valid:
  13. if len(v & set(combo)) > 1:
  14. break # this combo has more than 1 element in common
  15. else:
  16. valid.append(set(combo))
  17. # (m,n) is valid if all members of m are represented in the list of
  18. # valid combinations
  19. if len(functools.reduce(set.union, valid)) == m:
  20. print(f'({m}, {n}) is valid and produces {len(valid)} combinations')
  21. print(valid)

个字符

编辑:根据您的评论,为了有效地生成大量的有效组合,这里有一个替代实现。这个版本存储了少量的状态,并根据请求输出有效的组合。它仍然需要花费很长时间来生成所有的m,n = 96,6值,但每个单独的组合都可以执行,而不必计算它们。

  1. import itertools
  2. def valid_combos(m, n):
  3. seen_pairs = set() # this will only grow to C(m,2) elements
  4. # loop through every possible combination given (m, n)
  5. for combo in itertools.combinations(range(m), n):
  6. combo_pairs = set()
  7. for pair in itertools.combinations(combo, 2):
  8. if pair in seen_pairs:
  9. break
  10. combo_pairs.add(pair)
  11. else:
  12. seen_pairs |= combo_pairs
  13. yield combo
  14. for count, combo in enumerate(valid_combos(96, 5), start=1):
  15. print(combo)
  16. print(f'{count=}')


可以通过在生成器中使用多线程或DISC来进行一些优化。可以通过实现itertools.combinations的自定义版本来实现进一步的优化,其中可以将seen_pairs的检查集成到逻辑中,从而避免生成无效组合。

上次编辑:我尝试了一个修改过的itertools.combinations的参考实现,以从输入空间中删除块。下面是m,n=96,6的计时结果。

  1. import itertools
  2. def pruned_combinations(n, r, seen_pairs):
  3. """modified itertools.combinations reference implementation:
  4. https://docs.python.org/3/library/itertools.html#itertools.combinations
  5. """
  6. if r > n:
  7. return
  8. indices = list(range(r))
  9. yield tuple(indices)
  10. while True:
  11. for i in reversed(range(r)):
  12. if indices[i] != i + n - r:
  13. break
  14. else:
  15. return
  16. indices[i] += 1
  17. for j in range(i+1, r):
  18. indices[j] = indices[j-1] + 1
  19. # prune, if possible
  20. for z in range(1, r-2):
  21. if tuple(indices[z-1:z+1]) in seen_pairs:
  22. if indices[z] != z+n-r:
  23. indices[z] += 1
  24. for j in range(z+1, r):
  25. indices[j] = indices[j-1] + 1
  26. break
  27. else:
  28. yield tuple(indices)
  29. def valid_combos(m, n):
  30. seen_pairs = set() # this will only grow to C(m,2) elements
  31. for combo in pruned_combinations(m, n, seen_pairs):
  32. combo_pairs = set(itertools.combinations(combo, 2))
  33. for pair in combo_pairs:
  34. if pair in seen_pairs:
  35. break
  36. else:
  37. seen_pairs |= combo_pairs
  38. yield combo
  39. import timeit
  40. start_time = timeit.default_timer()
  41. for count, combo in enumerate(valid_combos(96, 6), start=1):
  42. pass
  43. print(f'{count=}')
  44. print(f'time: {timeit.default_timer()-start_time:.2f} seconds')
  1. count=211
  2. time: 32.66 seconds
展开查看全部

相关问题