我正在研究从0到最大显示所有narcissistic numbers的程序。其中max是用户输入的值。我现在已经获得了所有代码我正在努力改进它。因此,我几乎没有问题。
我需要检查数字的每个数字并使它们达到n次方。所以我决定创建包含indexOfTab到n次幂的tab [0..9],然后当我对数字中的所有数字求和时,它的工作原理如下:
sum := sum + tab[x]; //where x is the digit that is currently checked
现在我想知道比较是否比这更快:
sum:= sum + power(x,n);
如果总和溢出,我也想抓住。我知道如何通过if .. then
执行此操作。但我想知道是否有办法不检查每个操作,如果总和更改符号,只有该程序将捕获该变量溢出,然后它将执行一些代码。
编辑:
while (tmpNum>0) do //tmpNum - digit of currenlty checked number(curNum)
begin
try
suma:= suma+tab[tmpNum mod 10]; //suma =0
//tab[x] = x^(curNum.Length);
Except
suma:= 0;
tmpNum:=0;
//here do something more
end;
tmpNum:= tmpNum div 10; //divide number to get next modulo
end;
答案 0 :(得分:1)
首先,使用值表的方法是目前使用最快的方法。就溢出而言,我想很快回过头来。
如果你认真考虑尽可能快地跑步,可以通过稍微分析问题来提高速度。假设在知道如何找到所有值时找到所有值是非常诱人的,您只需要迭代所有值到最大值。我担心,这很有效,而且情况确实如此。
要确定这是真的,只需考虑计算数字1034,1304,1403和1340.所有计算实际上完全相同,因此我们可以通过仅检查按降序排列的数字来大大减少我们的计算(在我们的案例4310)。计算出4 ^ 4 + 3 ^ 4 + 1 ^ 4 + 0 ^ 4后,我们只需要检查结果是否包含数字4,3,1和0.如果确实如此,那么计算出的数字就是自恋。这个概念也有助于最小化溢出测试,因为这意味着如果8000,例如溢出,即使检查更大的数字也没有意义。另一方面,在短路和通过if语句引入复杂性之间存在平衡。
这方面的缺点是数字不是按顺序生成的,因此最后可能需要进行某种排序。 (我还没有这样做)。但是,在好的方面,它允许使用并行for循环。在实践中,这并没有节省太多时间(可能是我机器上的10%),因为使用多个线程的开销在很大程度上抵消了并行处理的收益。下面的代码显示了两种方式。
以下程序允许用户输入多个数字(而不是最大值)进行测试,并处理溢出。我这样做是为了简化编码。在我的机器上计算所有19位数的自恋< 2 ^ 63花了大约6秒钟。
unit UnitNarcisistCalc;
interface
uses
System.Classes,
System.SysUtils,
System.Threading,
System.SyncObjs;
type
TCalcArray = array[ 0..9] of int64;
TNarcisistCalc = class
(* Calculated narcisistic number of a certain size *)
private
class function CheckResult( const pSum : int64; const DigitsUsed : TCalcArray; const DigitCount : integer ) : boolean;
class procedure AddADigit( const pDigit, pDigitsLeft : integer; const pSumSoFar : int64;
const pPowers, DigitsUsed : TCalcArray;
const pResults : TStrings; const DigitCount : integer );
protected
public
class procedure CalcNos( const pOfSize : integer; const pResults : TStrings;
pParallel : boolean );
end;
implementation
{ TNarcisistCalc }
class procedure TNarcisistCalc.AddADigit(const pDigit, pDigitsLeft: integer;
const pSumSoFar: int64; const pPowers, DigitsUsed: TCalcArray;
const pResults: TStrings; const DigitCount : integer );
var
iNewSum : int64;
i : integer;
iDigitsUsed : TCalcArray;
iOverflowMsg : string;
j: Integer;
begin
{
This recursive function builds the sum progressively until
pDigitsLeft = 0; We are careful to make all parameters const
so we don't accidently reuse anything.
}
iDigitsUsed := DigitsUsed;
iNewSum := pSumSoFar + pPowers[ pDigit ];
inc( iDigitsUsed[ pDigit ]);
if iNewSum < 0 then
begin
// overflow - so ditch this strand.
iOverflowMsg := 'Overflowed while evaluating ';
for i := 9 downto 0 do
begin
for j := 1 to iDigitsUsed[ i ] do
begin
iOverflowMsg := iOverflowMsg+ IntToStr( i );
end;
end;
pResults.Add( iOverflowMsg );
exit;
end;
if pDigitsLeft > 1 then // because we are not descrementing pDigitsLeft left even though logically we should
begin
for i := 0 to pDigit do
begin
AddADigit( i, pDigitsLeft - 1, iNewSum, pPowers, iDigitsUsed, pResults, DigitCount + 1 );
end;
end
else
begin
// lowest level
if CheckResult( pSumSoFar, iDigitsUsed, DigitCount + 1 ) then
begin
pResults.Add( IntToStr( pSumSoFar ));
end;
end;
end;
class procedure TNarcisistCalc.CalcNos(const pOfSize: integer;
const pResults: TStrings; pParallel : boolean);
var
fPowers : TCalcArray;
fUsed : TCalcArray;
i: Integer;
j: Integer;
iMaxDigit : integer;
iOverflow : Boolean;
iSum : int64;
iOverflowMsg : string;
iStrings : array[ 0.. 9 ] of TStringList;
begin
// calculate the powwers
pResults.Clear;
iOverFlow := FALSE;
iMaxDigit := 0;
for i := 0 to 9 do
begin
fPowers[ i ] := i;
fUsed[ i ] := 0;
for j := 2 to pOfSize do
begin
fPowers[ i ] := fPowers[ i ] * i;
if fPowers[ i ] < 0 then
begin
// overflow
iOverflow := TRUE;
iOverflowMsg := 'Overflowed while evaluating ' + IntToStr( i ) + '^' + IntToStr( pOfSize );
pResults.Add( iOverflowMsg );
break;
end;
end;
if iOverflow then
begin
break;
end
else
begin
iMaxDigit := i;
end;
end;
// we have set up our tabs and also prepared to not test any digits that
// would automatically give an overflow
if pParallel then
begin
TParallel.&For( 1, iMaxDigit, procedure(I : Integer )
var
iSum : int64;
begin
iStrings[ i ] := TStringList.Create;
iSum := 0;
AddADigit( i, pOfSize, iSum, fPowers, fUsed, iStrings[ i ], 0 );
end);
for i := 1 to iMaxDigit do
begin
pResults.AddStrings( iStrings[ i ]);
iStrings[ i ].Free;
end;
end
else
begin
for i := 1 to iMaxDigit do
begin
iSum := 0;
AddADigit( i, pOfSize, iSum, fPowers, fUsed, pResults, 0 );
end;
end;
end;
class function TNarcisistCalc.CheckResult(const pSum: int64;
const DigitsUsed: TCalcArray; const DigitCount : integer): boolean;
var
iDigitsUsed : TCalcArray;
iDigit, iSum : int64;
iDigitCount : integer;
begin
{ what we are doing here is checking if pSum contains the
same digits that were used to create it in the first place. }
iDigitsUsed := DigitsUsed;
iDigitCount := DigitCount;
iSum := pSum;
while iSum > 0 do
begin
iDigit := iSum mod 10;
iSum := iSum Div 10;
if iDigitsUsed[ iDigit ] > 0 then
begin
dec( iDigitsUsed[ iDigit ]);
dec( iDigitCount );
end
else
begin
Result := FALSE;
exit;
end;
end;
Result := iDigitCount = 0;
end;
end.
了解这种方法与你的方法相比会很有趣。
19位数的结果如下所示:
(并行)