【问题标题】:how many numbers between 1 to 10 billion contains 141 到 100 亿之间有多少个数字包含 14
【发布时间】:2018-06-22 07:37:07
【问题描述】:

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

    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;
    }

请帮忙

【问题讨论】:

  • 您想知道有多少个数字包含“14”或多少次数字包含“14”?因为数字 1414 包含了两次。
  • @SergeyShevchenko 1414 将仅计为 1
  • 140 怎么样?你需要数他们吗?因为问题不够清楚
  • @PlexisPlexis 是的,我会数 140、1414、1014、10014000,任何数字都包含 14
  • 您可以在codegolf.stackexchange.com提出您的问题

标签: c# algorithm api counter


【解决方案1】:

我们可以检查所有

14包含多少个数

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

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 之间的任何数字。因此,在末尾包含 14 的区间中有 10^8 个数字。然后考虑???????14???????14??、...、14???????? 数字。 14 个序列有 9 个可能的位置。答案是 10^8 * 9 = 90000000。


[由马修·沃森添加]

这是 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
        }
    }
}

【讨论】:

  • @SomeshSharma,不,10000000000 有 11 位数字。
  • 872348501 的答案似乎是正确的(我的蛮力方法证实了这一点 - 见下文!)
  • 我冒昧地将 C# 实现添加到您的答案中。
  • 我认为你不需要 F(len, d, 1),因为你可以通过减去 sum(F(len, d, 0) for d = 0 来计算包含 14 的数字。 .9) 从 10^10-1.
【解决方案2】:

如果你想要一个蛮力解决方案,它可能是这样的(请注意,我们应该避免耗时字符串操作,如ToString、@987654322 @):

  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)

【讨论】:

    【解决方案3】:

    更新

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

    (上面有一个更好的解决方案,花费不到 100 毫秒,但我将把这个答案留在这里,因为它为快速答案提供了确凿的证据。)


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

    起初我以为我可以使用 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;
    }
    

    【讨论】:

    • 我也试过了,但差别不大
    • @MarcoSalerno 我猜你的电脑速度很慢......我的电脑用时不到 10 分钟。实际上,它的速度大约与您拥有的处理器内核数量有关。
    • 我的意思是计算速度足够快,如果在多线程版本中完成不会产生很大的影响,结果仍然需要太长时间
    • @MarcoSalerno 你真的应该在你的 OP 中定义什么是“太长”,因为它似乎是要求的一部分。
    • @MarcoSalerno 哦-在那种情况下,你怎么知道它不够快?我假设你是 OP,因为只有 OP 会知道这些细节......也许 10 分钟的计算时间就可以了(尽管显然有一个更好的解决方案,只需不到 100 毫秒!)
    【解决方案4】:

    您计算以 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。

    【讨论】:

      【解决方案5】:

      计算答案的一种简单方法是, 您可以在一个地方固定 14 个并计算剩余数字的组合, 并对所有可以放置 14 的位置执行此操作,以使数字仍然小于 10000000000,让我们举个例子,

      ***14*****,

      这里 14 之前的 '*' 可以用 900 种方式填充,14 之后的 * 可以用 10^5 种方式填充,因此总出现次数为 10^5*(900),

      同样你可以在其他位置固定 14 来计算结果,这个解决方案将非常快 O(10) 或简单 O(1),而之前的解决方案是 O(N),其中 N 是 10000000000

      【讨论】:

      • 让我们采用0-10000 范围。根据您的建议,答案是300。但是你不认为你把1414 数了两次吗?答案应该是299Check this
      • 您没有考虑到重复次数,而且您正在计算超过 100 亿的数字。
      【解决方案6】:

      您可以使用每 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)之外,“14”只有 1 个。

      【讨论】:

        猜你喜欢
        • 2021-11-06
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 2010-12-28
        • 2023-01-24
        • 2018-08-18
        • 1970-01-01
        • 2011-02-02
        相关资源
        最近更新 更多