具有一些约束的给定范围内的所有数字的总和

时间:2015-07-08 08:57:43

标签: algorithm math

我的目标是找到4到666554之间的所有数字的总和,其中包括4,5,6且满足以下约束条件。

数字出现次数的约束

  1. 时间数4出现在数字< = 1
  2. 时间数5出现在数字< = 2
  3. 时间数6出现在数字< = 3

    SUM = 4 + 5 + 6 + 45 + 46 + 54 + 55 + 56 + 64 + 65 + 66 + 454 + 455 + ................... .. + 666554。

  4. 简单的方法是运行循环并添加由4,5和6组成的数字,这些数字满足上述约束条件。

    long long sum = 0;
    for(int i=4;i <=666554;i++){
       /*check if number contains only 4,5 and 6 and constraints are not violated
         if condition is true then add the number to the sum*/
    }
    

    但似乎效率低下。检查号码是由4,5和6组成 并且数字不违反约束需要时间。有没有办法提高效率。我已经尝试了很多,但没有找到新的方法。请帮助。

4 个答案:

答案 0 :(得分:3)

你应该只检查由数字4,5和6组成的数字,其中只有几百个。

这并不太难:在你的循环中,不要只是将i增加1.

如果i%10&lt; 6然后你只需将我增加1.

否则,减去2(例如646变为644,6变成4);如果我&lt; 10然后加40,你完成了,如果我%100&lt; 60然后将我增加10,你就完成了。

否则,减去20(例如5466变为5464然后5444);如果我&lt; 100然后添加400,你就完成了,如果i%1000&lt; 600然后只增加100我就完成了。

依此类推。这样,你不会检查大约600,000个数字,但只检查大约800个。

更好的方法是找不到数字。我们来看看六位数字。数字4可以位于六个位置。数字5可以在(5 * 4)/ 2 = 10个位置,数字6必须在其余位置。因此,只有60个数字,包含60位数字4,120位数字5,180位数字6.每个数字在所有位置都会出现,因此总和为10 * 444444 + 20 * 555555 + 30 * 666666.您执行类似的计算1 .. 5位数。

答案 1 :(得分:2)

我认为生成所有这些数字的最简单方法是(通常是,当您需要迭代某些组合对象时)以递归方式迭代它们。写一个递归函数,接收“当前”数字,并在单独的变量中计算其中的数字6,5和4,将找到该数字的所有可能结尾并计算它们的总和。像这样的东西(伪代码):

func rec(current, n4, n5, n6)   // we have used n4 4's, n5 5's and n6 6's
                                // and formed the number current
                                // Let's account for it and add some more digits 
    if (n4>1)||(n5>2)||(n6>3) return 0   // no numbers satisfy the conditions
    // print "Started ", current, n4, n5, n6   // if you want to debug
    ans = current   // account for current number
    ans += rec(current*10+4, n4+1, n5, n6)   // append 4
    ans += rec(current*10+5, n4, n5+1, n6)   // append 5
    ans += rec(current*10+6, n4, n5, n6+1)   // append 6
    // print "Finished ", current, "answer=", ans   // if you want to debug
    return ans

...
answer = rec(0, 0, 0, 0)

这种方法在概念和编码方面都非常简单,并且可以非常容易地适应任何新的要求。

答案 2 :(得分:1)

一种“有趣”的方法:

l = [{0: [4, 5, 5, 6, 6, 6]}]

此处l将包含n个数字的所有数字,即l[0]包含带有“0”数字的数字(哲学类型......),l[1]一个数字,等等l[n]实际上是一个字典,其中键是数字本身,值是可用数字列表继续算法(但算法是什么?!)。

def run (l, n = None):
    if n is None:
        n = len(l[0][0])
    if n == 0:
        return l
    d = {}
    for k, r in l[-1].items(): # Retrieve the last column of l (the largest number)
        # k is the number, r the remaining values
        for i, v in enumerate(r):
            d[10 * k + v] = r[:i] + r[i+1:]
    l.append(d)
    return run (l, n - 1)

然后结果是l:

中所有dict的所有键的总和
l = run([{0: [4, 5, 5, 6, 6, 6]}])
r = sum(map(lambda d: sum(d.keys()), l))

这是评论部分讨论的答案:

def mySum (x, y, z):
    l = [{0: x * [4] + y * [5] + z * [6]}]
    l = run(l)
    return sum(map(lambda d: sum(d.keys()), l))

然后你只需:

x, y, z = raw_input().split()
print(mySum(int(x), int(y), int(z)))

答案 3 :(得分:0)

您可以使用排列预先生成数字。我为生成所有排列做了一个小例子,但是在中间循环或最后一个循环中,你可以计算4个,5个和6个的数字。或者为此创建一个单独的方法,但它会让你知道如何解决这个问题。通过使用哈希集,您将获得唯一的数字。

为了便于阅读,我建议您将其重写为带有计数器数字的递归方法。


    var numbers = " 456";
    HashSet perm = new HashSet();
    foreach(char n1 in numbers)
    {
        string val1 = Char.ToString(n1);
        if(val1.Substring(0,val1.Length).Contains(" "))
            continue;

        foreach(char n2 in numbers)
        {
            string val2 = val1;
            if(!val1.Substring(0,val1.Length-1).Contains(' '))
                val2 += n2;

            foreach(char n3 in numbers)
            {           
                string val3 = val2;
                if(!val2.Substring(0,val2.Length).Contains(" "))
                    val3 += n3;

                perm.Add(Int32.Parse(val3));
            }
        }
    }