Algorithm 如何使用符号表示整数+;*()和1,成本最低?

Algorithm 如何使用符号表示整数+;*()和1,成本最低?,algorithm,math,dynamic-programming,Algorithm,Math,Dynamic Programming,任务是从符号+*()(加法、乘法和括号)和数字1构建整数。您将获得一个整数,并且必须使用最小字符数输出表达式。例如: 4 = 1+1+1+1 23 = 11+11+1 242 = (11+11)*11 1000 = 1+(1+1+1)*(1+1+1)*111 1997 = (1+1)*(1+1+1)*111+11*11*11 您可以使用动态规划,为每个数字i

任务是从符号
+*()
(加法、乘法和括号)和数字
1
构建整数。您将获得一个整数,并且必须使用最小字符数输出表达式。例如:

4    = 1+1+1+1  
23   = 11+11+1  
242  = (11+11)*11  
1000 = 1+(1+1+1)*(1+1+1)*111 
1997 = (1+1)*(1+1+1)*111+11*11*11 

您可以使用动态规划,为每个数字i 这里有一些未优化的代码,可以打印到2000之前所有数字的最短解。它在我的笔记本电脑上运行的时间只有2秒多,但是从代码中删除所有字符串结构的空间很大。它在O(n^2)时间内运行


这是我的递归解决方案。在我的平板电脑上,它可以在1.4秒内处理2000个元素:

import math

def to_onestr(n, numbers=None, divs=None):
    if numbers is None:
        numbers = [None] * (n + 1)
        numbers[0] = ('', False)
    if divs is None:
        divs = get_divs(n)

    if numbers[n] is None:
        s = str(n)
        # Default representation is 11111 or 1+1+1+1
        if s == '1'*len(s): res = (s, False)
        else: res = ("+".join(['1'] * n), True)

        # Find all representations d*k + r, d < k
        for d in divs:
            if d >= n: break
            k, r = divmod(n, d)
            if k < d: d, k = k, d
            k_res, r_res, d_res = to_onestr(k, numbers, divs), to_onestr(r, numbers, divs), to_onestr(d, numbers, divs)

            res_str, res_bool = '', False
            if d != 1:
                res_str += '({})*'.format(d_res[0]) if d_res[1] else d_res[0] + '*'
            res_str += '({})'.format(k_res[0]) if k_res[1] else k_res[0]

            if d != 1 and len(k_res[0]) * d + d - 1 < len(res_str):
                res_str = '+'.join([k_res[0]]*d)
                res_bool = True

            if r != 0:
                res_str += '+{}'.format(r_res[0])
                res_bool = True
            if len(res_str) < len(res[0]):
                res = (res_str, res_bool)
        numbers[n] = res
    return numbers[n]

def get_divs(n):
    p = [1] * (n + 1)
    # Get all prime numbers + all numbers which contains only 1 + all numbers we could get from 11..1 by multiplication
    for i in range(2, int(math.ceil(math.sqrt(n)))):
        if p[i] == 1:
            for j in range(i * i, n, i):
                if j % i == 0:
                    p[j] = 0

    for x in xrange(2, len(str(n)) + 1):
        i = int('1'*x)
        j = i
        while j <= n:
            p[j] = 1
            j = j * i

    return [i for (i, v) in enumerate(p) if v == 1 and i > 1]
还测试了@Anonymous方法

>>> timeit('minconstruct(2000)', 'from __main__ import minconstruct', number=1)
12.012599471759474

漂亮的问题,离题了。我觉得这不是离题。只需为您尝试过的内容添加一些代码,它就完全符合主题了。@tcaswell(和其他亲密的投票者)算法设计非常适合这样做。不仅仅是“如何在C中解析字符串?”——这里欢迎更多的高级问题,因为它们与编程密切相关。一旦想法明确,从算法构造程序(代码)就很简单了。这个问题完全是针对主题的。11**11是这个任务的有效表达吗?我对这个问题有一个非常好的答案,但这个评论框太小,容纳不下它。投票重新打开。测试你的代码,但“len(str(n))+1])”有点问题,@Pythoner在提交它时不得不去做,改变了一点解决方案,它现在填充了从0到n的所有数字列表。该代码给出了“((11+1)*11+1)*(1+1+1+1)*(1+1+1)+1+1”,而不是“(1+1)*(1+1+1+1)*111+11*11”。它最小化了什么?@Anonymous是的,我认为我必须最小化1的数量。必须对新代码进行一些测试。@Anonymous修复了它,但代码并不像我希望的那样干净:(据我所知,这不是动态编程,而是简单的蛮力编程force@RomanPekar每个i的解决方案取决于已计算出的较低数字的结果。它怎么不是动态规划?为什么是向下投票?抱歉,虽然它工作不正确,但我已删除了向下投票(必须对其进行一点编辑)。您的实现很简洁,但仍然是蛮力式的,我的解决方案更快,但代码不是很优雅,以后必须检查是否可以使其更简洁。@RomanPekar:这绝对是动态编程。@Neig是的,我的错,下次再去复习Tim Roughgarden的课:)
>>> timeit('to_onestr(2000)', 'from __main__ import to_onestr', number=1)
1.1375278780336457
>>> timeit('to_onestr(4000)', 'from __main__ import to_onestr', number=1)
3.6481025870678696
>>> timeit('to_onestr(6000)', 'from __main__ import to_onestr', number=1)
7.732885259577177
>>> timeit('minconstruct(2000)', 'from __main__ import minconstruct', number=1)
12.012599471759474