【问题标题】:How to get maximum sum of a coprime subset of naturals less than n?如何获得小于n的自然数互质子集的最大和?
【发布时间】:2020-11-29 17:05:51
【问题描述】:

我需要一个列出给定数字的较小互质数的函数。例如,co(11) 给出[1,7,9,10],它的总和给出27。但我想获得产生最大总和的互质数。对于 co(11),它应该消除 10(因为 5+8 > 10)并返回 [1,5,7,8,9] 以获得最大和,即 30。 这是函数:

import math
def Co(n):
    Mylist = [x for x in range(1, n)]
    removeds  =[]
    for x in Mylist:
        y = Mylist.index(x)
        for z in Mylist[y+1:]:
            if math.gcd(x, z) != 1:
                removed = Mylist.pop(y)
                removeds.append(removed)
                #print(removed)
                Mylist[1:] = Mylist
                #print(Mylist)
                break
    
    Mylist= list(dict.fromkeys(Mylist))
    removeds = list(dict.fromkeys(removeds))
    removeds.sort(reverse = True)
    for a in removeds:
        check = []
        for b in Mylist:
           if math.gcd(a, b) != 1:
               break
           else:
               check.append(a)
        if len(check) == len(Mylist):
           Mylist.append(a)
           
      
    print(Mylist)
    print(sum(Mylist))
Co(11)

结果是:

[1, 7, 9, 10]
27

为了得到可能的互质集的最大总和,它应该返回

[1, 5, 7, 8, 9]
30

我考虑获取所有可能的互质数集,然后将它们进行比较以获得最大的总和。但是当Co(N) 变大时,它变得无法控制且效率不高。我知道这是比 python 更多的数学问题,但任何提示将不胜感激。

【问题讨论】:

    标签: python algorithm


    【解决方案1】:

    回溯有效,但要处理较大的 n,您必须小心分支策略(我使用 Bron–Kerbosch algorithm with pivoting 枚举最大团)并具有有效的修剪策略。我使用的修剪策略在一开始就为图表着色(我使用了greedy coloring in reverse degeneracy order)。为了计算 Bron-Kerbosch 的特定递归调用的界限,将已经选择的节点 (R) 和每种颜色的可能仍然选择的颜色的最大节点 (P) 相加,因为两个节点的颜色肯定相同不属于同一集团。

    在 Python 3 中:

    import math
    
    
    def coprime_graph(n):
        return {
            i: {j for j in range(1, n) if j != i and math.gcd(j, i) == 1}
            for i in range(1, n)
        }
    
    
    def degeneracy_order(g):
        g = {v: g_v.copy() for (v, g_v) in g.items()}
        order = []
        while g:
            v, g_v = min(g.items(), key=lambda item: len(item[1]))
            for w in g_v:
                g[w].remove(v)
            del g[v]
            order.append(v)
        return order
    
    
    def least_non_element(s):
        s = set(s)
        i = 0
        while i in s:
            i += 1
        return i
    
    
    def degeneracy_coloring(g):
        coloring = {}
        for v in reversed(degeneracy_order(g)):
            coloring[v] = least_non_element(coloring.get(w) for w in g[v])
        return coloring
    
    
    def max_cliques(g, coloring, bound, r, p, x):
        if not p and not x:
            yield r
    
        best = {}
        for v in p:
            i = coloring[v]
            if v > best.get(i, 0):
                best[i] = v
        if sum(r) + sum(best.values()) <= bound[0]:
            return
    
        u_opt = min(p | x, key=lambda u: len(p - g[u]))
        for v in sorted(p - g[u_opt], reverse=True):
            p.remove(v)
            yield from max_cliques(g, coloring, bound, r | {v}, p & g[v], x & g[v])
            x.add(v)
    
    
    def max_sum_clique(g):
        coloring = degeneracy_coloring(g)
        bound = [0]
        best_so_far = set()
        for clique in max_cliques(g, coloring, bound, set(), set(g), set()):
            objective = sum(clique)
            if objective > bound[0]:
                bound[0] = objective
                best_so_far = clique
        return best_so_far
    
    
    def main(n):
        print(max_sum_clique(coprime_graph(n)))
    
    
    if __name__ == "__main__":
        main(500)
    

    【讨论】:

    • 感谢您的解释和努力,有很多事情要理解。但是当我尝试main(30) 时,它返回{1, 2, 5, 7, 9} 是迄今为止最好的,这实际上不是最好的。我错过了关于整个功能的一些东西吗?
    • @fatiharslan 修复了一个错误——我在 max_cliques 中迭代了错误的顶点 v 集。
    • 非常感谢。我想我至少需要 1 周的时间来理解这段代码的作用:)
    • 很好 :) (填充最小评论长度)
    • 测试了多达 450 个,结果集中的所有数字最多有两个素因子,这可能会导致更有效的算法。我想知道是否有办法证明对于更大的数字也是如此。
    【解决方案2】:

    我实际上并没有仔细检查您的代码,我认为它没有按预期工作,因为一旦它在解决方案中广告了适合的元素,它就不会返回(以寻找更好的替代品),所以您不知何故陷入了 1st 解决方案。
    有很多方法可以解决这类问题,我将使用Backtracking

    关于它的几点说明:

    • 很简单(来自我的PoV
    • 生成所有可能的解决方案(这可能是它的优势,也可能是它的缺陷)
    • 效率非常低(对于我们的问题),一般来说,它相当于蛮力。对于较高的 n 值,时间复杂度将呈指数增长

    另外,回溯可以有多种形式,我选择的是循环的。

    code00.py

    #!/usr/bin/env python
    
    import sys
    from math import gcd
    
    
    def is_valid(item, arr):
        for elem in arr:
            if gcd(elem, item) > 1:
                return False
        return True
    
    
    def bt(n, sols, cur_sol):
        if cur_sol:
            sols.add(tuple(cur_sol))
        for i in range(cur_sol[-1] + 1 if cur_sol else 1, n):
            if is_valid(i, cur_sol):
                cur_sol.append(i)
                bt(n, sols, cur_sol)
        else:
            if cur_sol:
                cur_sol.pop()
    
    
    def main(*argv):
        solutions = set()
        current_solution = []
        n = 11
        start_time = time.time()
        bt(n, solutions, current_solution)
        end_time = time.time()
        print("Solutions:", sorted(solutions))
        print("\nFor n={0:d}, it took {1:.6f} seconds".format(n, end_time - start_time))
        best_solution = max(solutions, key=sum)
        print("\nBest solution: {0:}\nSum: {1:}".format(best_solution, sum(best_solution)))
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main(*sys.argv[1:])
        print("\nDone.")
    

    输出

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q065062781]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Solutions: [(1,), (1, 2), (1, 2, 3), (1, 2, 3, 5), (1, 2, 3, 5, 7), (1, 2, 3, 7), (1, 2, 5), (1, 2, 5, 7), (1, 2, 5, 7, 9), (1, 2, 5, 9), (1, 2, 7), (1, 2, 7, 9), (1, 2, 9), (1, 3), (1, 3, 4), (1, 3, 4, 5), (1, 3, 4, 5, 7), (1, 3, 4, 7), (1, 3, 5), (1, 3, 5, 7), (1, 3, 5, 7, 8), (1, 3, 5, 8), (1, 3, 7), (1, 3, 7, 8), (1, 3, 7, 10), (1, 3, 8), (1, 3, 10), (1, 4), (1, 4, 5), (1, 4, 5, 7), (1, 4, 5, 7, 9), (1, 4, 5, 9), (1, 4, 7), (1, 4, 7, 9), (1, 4, 9), (1, 5), (1, 5, 6), (1, 5, 6, 7), (1, 5, 7), (1, 5, 7, 8), (1, 5, 7, 8, 9), (1, 5, 7, 9), (1, 5, 8), (1, 5, 8, 9), (1, 5, 9), (1, 6), (1, 6, 7), (1, 7), (1, 7, 8), (1, 7, 8, 9), (1, 7, 9), (1, 7, 9, 10), (1, 7, 10), (1, 8), (1, 8, 9), (1, 9), (1, 9, 10), (1, 10), (2,), (2, 3), (2, 3, 5), (2, 3, 5, 7), (2, 3, 7), (2, 5), (2, 5, 7), (2, 5, 7, 9), (2, 5, 9), (2, 7), (2, 7, 9), (2, 9), (3,), (3, 4), (3, 4, 5), (3, 4, 5, 7), (3, 4, 7), (3, 5), (3, 5, 7), (3, 5, 7, 8), (3, 5, 8), (3, 7), (3, 7, 8), (3, 7, 10), (3, 8), (3, 10), (4,), (4, 5), (4, 5, 7), (4, 5, 7, 9), (4, 5, 9), (4, 7), (4, 7, 9), (4, 9), (5,), (5, 6), (5, 6, 7), (5, 7), (5, 7, 8), (5, 7, 8, 9), (5, 7, 9), (5, 8), (5, 8, 9), (5, 9), (6,), (6, 7), (7,), (7, 8), (7, 8, 9), (7, 9), (7, 9, 10), (7, 10), (8,), (8, 9), (9,), (9, 10), (10,)]
    
    For n=11, it took 0.000000 seconds
    
    Best solution: (1, 5, 7, 8, 9)
    Sum: 30
    
    Done.
    

    【讨论】:

    • 感谢您的回答。这是我的想法,但无法弄清楚如何在python中做到这一点。但是当 N 变大时,这个解决方案就会失败。例如,当你给 n=100 时,它会永远返回一些东西。
    • 嗯,没错。那么,显然这不是要走的路。
    • 但请记住,(我不这么认为)有一种方法适用于任何 n。那么预期的 max n 是多少(以及预期的时间是多少)?
    • 其实max n200000,时间并不重要。
    猜你喜欢
    • 2013-09-18
    • 1970-01-01
    • 2017-09-09
    • 2016-12-28
    • 2023-03-17
    • 1970-01-01
    • 2019-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多