1到100亿之间有多少个数字包含14

时间:2018-06-22 07:37:08

标签: c# algorithm api counter

我尝试了这段代码,但是花了很长时间,我无法得到结果

    public long getCounter([FromBody]object req)
    {
        JObject param = Utility.GetRequestParameter(req);
        long input = long.Parse(param["input"].ToString());
        long counter = 0;
        for (long i = 14; i <= input; i++)
        {
            string s = i.ToString();
            if (s.Contains("14"))
            {
                counter += 1;
            }
        }
        return counter;
    }

请帮助

6 个答案:

答案 0 :(得分:9)

我们可以检查所有<10 ^ 10的非负数。每个这样的数字都可以用10位数字的序列表示(允许前导零)。

多少个数字包括14

动态编程解决方案。让我们找到以特定数字结尾并包含(或不包含)子序列14的特定长度的序列数: enter image description here

F(len, digit, 0)是长度为len并以digit结尾且不包含14的序列数,F(len, digit, 1)是包含14的此类序列数。 F(0, 0, 0) = 1。结果是所有F(10, digit, 1)的总和。

要使用的C ++代码:https://ideone.com/2aS17v。答案似乎是872348501。

数字包含14次的次数

首先,让我们在序列的末尾放置14:

????????14

每个'?'可以用0到9之间的任何数字替换。因此,间隔中有10 ^ 8个数字,末尾包含14个数字。然后考虑???????14???????14??,...,14????????个数字。有14个序列的9个可能位置。答案是10 ^ 8 * 9 = 90000000。


[由Matthew Watson添加]

这是C ++实现的C#版本;它运行不到100毫秒:

using System;

namespace Demo
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            const int M = 10;
            int[,,] f = new int [M + 1, 10, 2];

            f[0, 0, 0] = 1;

            for (int len = 1; len <= M; ++len)
            {
                for (int d = 0; d <= 9; ++d)
                {
                    for (int j = 0; j <= 9; ++j)
                    {
                        f[len,d,0] += f[len - 1,j,0];
                        f[len,d,1] += f[len - 1,j,1];
                    }
                }
                f[len,4,0] -= f[len - 1,1,0];
                f[len,4,1] += f[len - 1,1,0];
            }

            int sum = 0;

            for (int i = 0; i <= 9; ++i)
                sum += f[M,i,1];

            Console.WriteLine(sum); // 872,348,501
        }
    }
}

答案 1 :(得分:5)

如果您想要一个
强力解决方案,则可能是这样的(请注意,我们应该避免使用诸如ToString之类的耗时的字符串操作, Contains):

  int count = 0;

  // Let's use all CPU's cores: Parallel.For 
  Parallel.For(0L, 10000000000L, (v) => {  
    for (long x = v; x > 10; x /= 10) {
      // Get rid of ToString and Contains here
      if (x % 100 == 14) {
        Interlocked.Increment(ref count); // We want an atomic (thread safe) operation

        break;
      }
    }
  });

  Console.Write(count);

它会在 6 分钟内返回872348501(Core i7拥有4个3.2GHz内核)

答案 2 :(得分:4)

更新

我的并行代码在我的8处理器核心Intel Core I7 PC上在9分钟内将结果计算为872,348,501

(上面有一个更好的解决方案,它花费的时间少于100ms,但我将在此处保留此答案,因为它为快速答案提供了确凿的证据。)


您可以使用多个线程(每个处理器内核一个线程)来减少计算时间。

起初我以为我可以使用AsParallel()来加快速度-但是,事实证明,对于超过2 ^ 31个项目的序列,您不能使用AsParallel()

(为完整起见,在此答案的结尾处,我包括使用AsParallel的错误实现)。

相反,我编写了一些自定义代码将问题分解为与处理器数量相等的多个块:

using System;
using System.Linq;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            int numProcessors = Environment.ProcessorCount;

            Task<long>[] results = new Task<long>[numProcessors];

            long count = 10000000000;
            long elementsPerProcessor = count / numProcessors;

            for (int i = 0; i < numProcessors; ++i)
            {
                long end;
                long start = i * elementsPerProcessor;

                if (i != (numProcessors - 1))
                    end = start + elementsPerProcessor;
                else // Last thread - go right up to the last element.
                    end = count;

                results[i] = Task.Run(() => processElements(start, end));
            }

            long sum = results.Select(r => r.Result).Sum();

            Console.WriteLine(sum);
        }

        static long processElements(long inclusiveStart, long exclusiveEnd)
        {
            long total = 0;

            for (long i = inclusiveStart; i < exclusiveEnd; ++i)
                if (i.ToString().Contains("14"))
                    ++total;

            return total;
        }
    }
}

以下代码不起作用,因为AsParallel()不适用于超过2 ^ 31个项目的序列。

static void Main(string[] args)
{
    var numbersContaining14 =
        from number in numbers(0, 100000000000).AsParallel()
        where number.ToString().Contains("14")
        select number;

    Console.WriteLine(numbersContaining14.LongCount());
}

static IEnumerable<long> numbers(long first, long count)
{
    for (long i = first, last = first + count; i < last; ++i)
        yield return i;
}

答案 3 :(得分:3)

您可以计算以1、4或其他不包含14的给定长度的数目计数。然后可以将长度扩展1。

然后要做包含14的数字计数就是所有数字的计数减去不包含14的数字。

private static long Count(int len) {
   long e1=0, e4=0, eo=1;
   long N=1;
   for (int n=0; n<len; n++) {
       long ne1 = e4+e1+eo, ne4 = e4+eo, neo = 8*(e1+e4+eo);
       e1 = ne1; e4 = ne4; eo = neo;
       N *= 10;
   }
   return N - e1 - e4 - eo;
}

您可以减少这段代码,注意eo = 8*e1除了第一次迭代外,然后避免使用局部变量。

private static long Count(int len) {
   long e1=1, e4=1, N=10;
   for (int n=1; n<len; n++) {
       e4 += 8*e1;
       e1 += e4;
       N *= 10;
   }
   return N - 9*e1 - e4;
}

对于这两种情况,Count(10)返回872348501。

答案 4 :(得分:0)

一种简单的计算答案的方法是 您可以将14个数字固定在一个位置,然后计算剩余数字的总和, 并针对所有可能放置14个位置(以使该数量仍小于10000000000)的位置执行此操作,让我们举个例子,

*** 14 *****,

这里14之前的'*'可以用900填充,而14之后的*可以用10 ^ 5填充,所以总出现次数是10 ^ 5 *(900),

类似地,您可以在其他位置固定14个以计算结果,并且该解决方案将非常快O(10)或只是O(1),而先前的解决方案是O(N),其中N为10000000000

答案 5 :(得分:0)

您可以使用以下事实:每1000个(即1到1000以及1001到2000等) 找到14个:19次,所以当您收到输入时,将其除以1000,例如,您收到1200,所以1200/1000 结果为1,余数为200,因此我们有1 * 19个“ 14”,然后可以循环遍历200。

您可以扩展10000(即计算10000中有多少个“ 14”并将其固定为全局变量),然后开始除以10000,然后应用上面的公式,然后将余数除以1000,然后应用方程式,然后将两个结果相加。

您可以将其扩展为以下事实:对于所有数百个(即从1到100以及从201到300),除了第二个百(101到200)之外,仅找到1表示“ 14”。

相关问题