我尝试了这段代码,但是花了很长时间,我无法得到结果
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;
}
请帮助
答案 0 :(得分:9)
我们可以检查所有<10 ^ 10的非负数。每个这样的数字都可以用10位数字的序列表示(允许前导零)。
多少个数字包括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之间的任何数字替换。因此,间隔中有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”。