欧拉计划:纠结的第23题,找出所有不能表示为两个过剩数之和的正整数之和,优化后7s可以执行完,再优化5秒

101 篇文章 7 订阅
52 篇文章 0 订阅

如果一个数的所有真因子之和等于这个数,那么这个数被称为完全数。例如,28的所有真因子之和为1 + 2 + 4 + 7 + 14 = 28,所以28是一个完全数。

如果一个数的所有真因子之和小于这个数,称其为不足数,如果大于这个数,称其为过剩数。

12是最小的过剩数,1 + 2 + 3 + 4 + 6 = 16。因此最小的能够写成两个过剩数之和的数字是24。经过分析,可以证明所有大于28123的数字都可以被写成两个过剩数之和。但是这个上界并不能被进一步缩小,即使我们知道最大的不能表示为两个过剩数之和的数字要比这个上界小。

找出所有不能表示为两个过剩数之和的正整数之和。


纠结并不是因为这道题目有多么难,程序很快就写出来了,

分为两部,首先求出小于28123的过剩数。并把他们放在数组和哈希中,放在数组中是为了遍历,放在hash中是为了查找方便

其次,遍历1到28123,遍历数组,如果当前数减去数组里的一个数载哈希中存在,那么久pass。

这个程序的效率不高。有1分钟以上。

程序如下:

use strict;
use warnings;

my %hash;
my $num;
my $m;
my @array;
my $cout=0;
for($num=12;$num<28123;$num++)
{
	my $sum=1 ;
	for($m=2;$m<$num/2+1;$m++)
	{
		if(0==$num%$m && $m<$num/$m)  
		{
			$sum=$sum+$m+$num/$m;
		}
		elsif(0==$num%$m && $m==$num/$m)#这个地方是一大败笔,没有考虑到相等的情况,在排除的时候对照百度的过剩数,发现少了196,才修改过来。
		{
			$sum=$sum+$m;
		}
		else
		{
			next;
		}
	}
	if($sum>$num)
	{
		$cout=$cout+1;
		$hash{$num}=$num;
		$array[$cout]=$num;
	}
}
foreach(1..200)
{
	print "$array[$_] ";
}
my $flag;
my $result=0;
my $i;
my $j;

for($i=1;$i<28123;$i++)
{
    my $flag=0;
	foreach(1..$cout)
	{
		$j=$i-$array[$_];
		if($j>10 && exists $hash{$j})
		{
			$flag=1;
		    last;
		}
	}
	if($flag==1)
	{
		next;
	}
	else
	{
		$result=$result+$i;
		last if($result>4179870)
	}
	
}

print "$result\n";

结果如下:


C:\WINDOWS\system32\cmd.exe /c perl "C:\Documents and Settings\Administrator\桌
面\names.pl"
12 18 20 24 30 36 40 42 48 54 56 60 66 70 72 78 80 84 88 90 96 100 102 104 108 1
12 114 120 126 132 138 140 144 150 156 160 162 168 174 176 180 186 192 196 198 2
00 204 208 210 216 220 222 224 228 234 240 246 252 258 260 264 270 272 276 280 2
82 288 294 300 304 306 308 312 318 320 324 330 336 340 342 348 350 352 354 360 3
64 366 368 372 378 380 384 390 392 396 400 402 408 414 416 420 426 432 438 440 4
44 448 450 456 460 462 464 468 474 476 480 486 490 492 498 500 504 510 516 520 5
22 528 532 534 540 544 546 550 552 558 560 564 570 572 576 580 582 588 594 600 6
06 608 612 616 618 620 624 630 636 640 642 644 648 650 654 660 666 672 678 680 6
84 690 696 700 702 704 708 714 720 726 728 732 736 738 740 744 748 750 756 760 7
62 768 770 774 780 784 786 792 798 800 804 810 812 816 820 4179871
Hit any key to close this window...




结果是4179871


用了173s。还要继续优化。


优化的地方在于遍历着过剩数的时候,上面用了$num/2+1

优化后改成了$num**0.5+1;

程序如下:

use strict;
use warnings;

my $start_time=time;
my %hash;
my $num;
my $m;
my @array;
my $cout=0;
for($num=12;$num<28123;$num++)
{
	my $sum=1 ;
	for($m=2;$m<$num**0.5+1;$m++)
	{
		if(0==$num%$m && $m<$num/$m)  
		{
			$sum=$sum+$m+$num/$m;
		}
		elsif(0==$num%$m && $m==$num/$m)
		{
			$sum=$sum+$m;
		}
		else
		{
			next;
		}
	}
	if($sum>$num)
	{
		$cout=$cout+1;
		$hash{$num}=$num;
		$array[$cout]=$num;
	}
}
foreach(1..200)
{
	print "$array[$_] ";
}
my $flag;
my $result=0;
my $i;
my $j;

for($i=1;$i<28123;$i++)
{
    my $flag=0;
	foreach(1..$cout)
	{
		$j=$i-$array[$_];
		if($j>10 && exists $hash{$j})
		{
			$flag=1;
		    last;
		}
	}
	if($flag==1)
	{
		next;
	}
	else
	{
		$result=$result+$i;
		last if($result>4179870)
	}
	
}

print "$result\n";
my $long=time-$start_time;
 print "$long\n";
结果如下:

C:\WINDOWS\system32\cmd.exe /c perl "C:\Documents and Settings\Administrator\桌
面\names.pl"
12 18 20 24 30 36 40 42 48 54 56 60 66 70 72 78 80 84 88 90 96 100 102 104 108 1
12 114 120 126 132 138 140 144 150 156 160 162 168 174 176 180 186 192 196 198 2
00 204 208 210 216 220 222 224 228 234 240 246 252 258 260 264 270 272 276 280 2
82 288 294 300 304 306 308 312 318 320 324 330 336 340 342 348 350 352 354 360 3
64 366 368 372 378 380 384 390 392 396 400 402 408 414 416 420 426 432 438 440 4
44 448 450 456 460 462 464 468 474 476 480 486 490 492 498 500 504 510 516 520 5
22 528 532 534 540 544 546 550 552 558 560 564 570 572 576 580 582 588 594 600 6
06 608 612 616 618 620 624 630 636 640 642 644 648 650 654 660 666 672 678 680 6
84 690 696 700 702 704 708 714 720 726 728 732 736 738 740 744 748 750 756 760 7
62 768 770 774 780 784 786 792 798 800 804 810 812 816 820 4179871
7
Hit any key to close this window...




又进行了一点优化:再找最后的结果的时候,刚开始时把所有的过剩数都循环,

这里改成循环一半;

程序如下:

use strict;
use warnings;

my $start_time=time;
my %hash;
my $num;
my $m;
my @array;
my $cout=0;
for($num=12;$num<28123;$num++)
{
	my $sum=1 ;
	for($m=2;$m<$num**0.5+1;$m++)
	{
		if(0==$num%$m && $m<$num/$m)  
		{
			$sum=$sum+$m+$num/$m;
		}
		elsif(0==$num%$m && $m==$num/$m)
		{
			$sum=$sum+$m;
		}
		else
		{
			next;
		}
	}
	if($sum>$num)
	{
		$cout=$cout+1;
		$hash{$num}=$num;
		$array[$cout]=$num;
	}
}
my $flag;
my $result=0;
my $i;
my $j;

for($i=1;$i<28123;$i++)
{
    my $flag=0;
	if($i<2*$cout)
	{
		foreach(1..$i/2+1)
		{
			$j=$i-$array[$_];
		    if($j>10 && exists $hash{$j})
		    {
				$flag=1;
		        last;
		    }
	    }
    }
	else
	{
		foreach(1..$cout)
		{
			$j=$i-$array[$_];
		    if($j>10 && exists $hash{$j})
		    {
				$flag=1;
		        last;
		    }
	    }

	}
	if($flag==1)
	{
		next;
	}
	else
	{
		$result=$result+$i;
		last if($result>4179870)
	}
	
}

print "$result\n";
my $long=time-$start_time;
 print "$long\n";

结果如下:

C:\WINDOWS\system32\cmd.exe /c perl "C:\Documents and Settings\A
面\names.pl"
4179871
5
Hit any key to close this window...




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值