以下代码旨在查找数字乘积为偶数的l
和r
之间的总数(针对多个测试用例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;
}
答案 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。因此,我们可以只计算由数字1
,3
,5
,7
和9
组成的数字,而不是枚举数字。对于整数N > 1
,正好有10^N - 10^(N-1)
个数字,N
个数字。在这些数字中,5 ^ N
是dodd,因此10^N - 10^(N-1) - 5^N
是deven。
方法是计算left
和right
边界之间有多少个dodd数,然后从left
和{{1}之间的总数中减去该数量}}。你也可以只计算deven数字,但这有点棘手。
实际上,你将使用这种方法循环数字,而不是通过数字。我在Python中的implementation能够在一秒钟内计算right
和1
( 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;
}