如何使这个甚至计算代码更快?

时间:2013-05-20 10:27:36

标签: c++ count numbers

以下代码旨在查找数字乘积为偶数的lr之间的总数(针对多个测试用例t)。此代码运行完美但r大于100000时速度非常慢。有人可以提出更好的选择吗?

#include <iostream>
#include <algorithm>
using namespace std;
long long int nd(long long int x, int n) //return the digit at a particular index    staring with zero as index for unit place
{
while (n--) {
    x /= 10;
}
return (x % 10);
}
int ng(long long int number) //returns total number of digits in an integer
{
int digits = 0;
if (number < 0) digits = 1;
while (number) {
    number /= 10;
    digits++;
}
return digits;
}

int main()
{
int t;
cin>>t;
long long int l[t], r[t], c;
for(long long int j=0;j<t;j++)
{
    cin>>l[j]>>r[j];
}
for(long long int k=0;k<t;k++)
{    
  long long int sum=0;
  long long int t=0;

  for(long long int i=l[k];i<=r[k];i++)
  {
   while(t<ng(i))
   {
       c=nd(i,t);
       if((c%2)==0)
       {
           ++sum;
           break;
       }
       ++t;
    }
   t=0;    
  }
 cout<<sum<<endl;
}    
cin.ignore();
cin.get();
return 0;
}            

7 个答案:

答案 0 :(得分:1)

基本思想是遍历数字的每个数字,看看它是否均匀。如果是,整个产品将是均匀的,并且不需要检查剩余的数字。

您的代码存在的问题是您多次搜索该数字以查找索引为i的数字。一旦检查了整体的均匀度,你应该简单地浏览一下数字的数字。

这是一个实现算法的不言自明的Go代码:

package main

func iseven(num int) bool {
    for num > 0 {
        digit := num % 10
        if digit&1 == 0 {  # same as digit%2 == 0, only simpler
            return true
        }
        num /= 10
    }
    return false
}

func main() {
    sum := 0
    for n := 1; n < 1000000; n++ {
        if iseven(n) {
            sum++
        }
    }
    println(sum)
}

我的机器上的性能:

λ time go run main.go
980469
go run main.go  0.05s user 0.01s system 81% cpu 0.073 total

<强>更新

如果你需要使用巨大的数字,那么可以使用更有效的方法。

让我们将具有数字乘积的数字称为奇数 dodd数字。因此,135是一个dodd数字,134不是。同样,具有数字乘积的数字甚至称为 deven 。所以134是一个deven号。

如前所述,只有由奇数组成的数字才是dodd。因此,我们可以只计算由数字13579组成的数字,而不是枚举数字。对于整数N > 1,正好有10^N - 10^(N-1)个数字,N个数字。在这些数字中,5 ^ N是dodd,因此10^N - 10^(N-1) - 5^N是deven。

方法是计算leftright边界之间有多少个dodd数,然后从left和{{1}之间的总数中减去该数量}}。你也可以只计算deven数字,但这有点棘手。

实际上,你将使用这种方法循环数字,而不是通过数字。我在Python中的implementation能够在一秒钟内计算right1 10000 位数字)之间的deven数量。

答案 1 :(得分:0)

基于以下内容的优化可行:

将两个数字相乘可以根据规则获得奇数/均匀度

even * even = even
odd * even = even * odd = even
odd * odd = odd

因此,您只需跟踪号码的最后一位数字。

我太老了,无法对此进行编码,但我敢打赌它会非常快,因为你只需要考虑0到9之间的数字。

答案 2 :(得分:0)

我无法弄清楚你的代码在做什么,但基本的 原则很简单:

  • value % 10是低位数字
  • value /= 10删除低位数字
  • 如果任何数字均匀,那么产品将是均匀的。

这会导致每个值的循环非常简单。 (你可以 必须特殊情况0。)

可以进一步优化:所有偶数都有 数字的乘积是偶数,所以你可以迭代 步骤2,然后加入均匀数量(一半) 范围)之后。这应该加快速度。

进一步优化:如果低位数是偶数,则数字本身是偶数,因此您不必提取低位数来测试它。

答案 3 :(得分:0)

您唯一需要检查的是数字中的一个数字是否均匀。如果是,它将有2作为因子,因此是均匀的。

您似乎还记不起数字位置 - 每次在t循环中增加for,然后拨打nd(i,t),您就会倒计时t中的nd为零。在最坏的情况下,这是数字的二次方。更好的方法是在开头简单地将数字分解为其组成数字。

答案 4 :(得分:0)

所有以例如10 ...,12 ...,14 ......,...,2 ...,30 ......开头的数字已知具有数字的偶数乘积。因此,我将从左侧(更高位数)开始并计算块数。只有少数数字的数字乘积是奇数(例如1111111111),只有在这里你需要深入挖掘。

这是一些伪代码:

int count(String prefix, int digits) {
  int result = 0;
  if (digits == 0)
    return 0;
  for (int d = 0; d < 10; d++) {
    if (d%2 == 0)
      result += 10**(digits-1);
    else
      result += count(prefix . toString(d), digits-1);
  }
  return result;
}

这将被称为count("2", 8),以获取从200000000到299999999的间隔计数。


这是整个块的Haskell实现(即所有 d - 数字):

blockcount :: Integer -> Integer
blockcount 0 = 0
blockcount d = 5 * 10^(d-1) + 5 * blockcount (d-1)

如,blockcount 1000被计算为99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999990667363814967811210099104552761828303829085536282919753782856602040330890242243655455596729021188976404050100696757573757845124786459676051584791827960692437655893338616748497260049240140981684888995092037348868817594874852040662091948217288745848961893016211455735188805301857713390407779823370895572015438305511128525334719936716315473525707381701378347972068047105063928821493363312589345601944692818636794001551739580458987867703701304978054853900957853913316387552070479651731353823420730839525799340636109582621041778816349219544433715557260746124828721452032184436535962851223182331001446079307345605759912880263252982501373733092527032374641960706237661660 18953072125441394746303558349609375,不到一秒钟。

您仍然需要添加将范围分解为合适块的代码。

答案 5 :(得分:0)

你可以做的另一件事是改变

  while(t<ng(i))

int d = ng(i);
while (t < d)

因此每次循环只调用一次ng。

此外,ng只是log(数字)+1(原木基数10)

我不知道那会更快。

答案 6 :(得分:0)

首先,请修改您的缩进

您的代码使用过多的除法和循环会导致很多延迟

long long int nd(long long int x, int n) //return the digit at a particular index    staring with zero as index for unit place
{
    while (n--) {
        x /= 10;
    }
    return (x % 10);
}

这可以通过表格查找轻松修复

long long int nd(long long int x, int n) //return the digit at a particular index    staring with zero as index for unit place
{
    long long int pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
                             100000000, 1000000000, 10000000000, 100000000000,
                             1000000000000, 10000000000000, 100000000000000,
                             1000000000000000, 10000000000000000,
                             100000000000000000, 1000000000000000000};

    return ((x / pow10[n]) % 10);
}

同样,获取整数中总位数的ng函数可以更改为快log10,无需重复分割和计数。当然,它需要一个小的改变来适应64位数

int ng(long long int number) //returns total number of digits in an integer
{
    int digits = 0;
    if (number < 0) digits = 1;
    while (number) {
        number /= 10;
        digits++;
    }
    return digits;
}