Javascript 电源表的最后一位数字

Javascript 电源表的最后一位数字,javascript,arrays,math,exponent,Javascript,Arrays,Math,Exponent,问题概述: 请注意,我将滥用^的寿命,并将其用作幂符号,尽管插入符号是JS中的按位异或运算符 取一个正整数列表 [ x_0, x_1, ..., x_n ] 并找到方程的最后一位,由 x_0 ^ ( x_1 ^ (... ^ x_n ) ... ) 我将调用此函数LD(…),以了解此问题的其余部分 例如:对于整数列表a=[2,2,2,2],如果2^(2^(2^2))=65536,很容易看出LD(a)=6 注意这个问题的0^0==1,与x^0==1一致,但与0^x==0不一致 我迄今为止所取

问题概述:

请注意,我将滥用
^
的寿命,并将其用作幂符号,尽管插入符号是JS中的按位异或运算符

取一个正整数列表

[ x_0, x_1, ..., x_n ]
并找到方程的最后一位,由

x_0 ^ ( x_1 ^ (... ^ x_n ) ... )
我将调用此函数
LD(…)
,以了解此问题的其余部分

例如:对于整数列表
a=[2,2,2,2]
,如果
2^(2^(2^2))=65536
,很容易看出
LD(a)=6

注意这个问题的
0^0==1
,与
x^0==1
一致,但与
0^x==0
不一致


我迄今为止所取得的成就

很容易得出结论,
x^0===1
,不管怎样

如果你做几个测试用例,也很容易得出结论,幂的最后几位“循环”:

LD(2 ^ 1) = 2,
LD(2 ^ 2) = 4,
LD(2 ^ 3) = 8,
LD(2 ^ 4) = 6,
LD(2 ^ 5) = 2, // Notice we've looped from hereon
LD(2 ^ 6) = 4,
LD(2 ^ 7) = 8,
...
因此,如果我们知道某个特定基数的循环中的数字计数(对于上述基数2的示例,为4),我们可以使用该计数的模计算出最后一个数字

例如,
LD(2^55)==LD(2^(55%4))==LD(2^3)

因此,通过一点数学,我们可以为每一个最后的数字得到一个很好的数组,其中数组的索引是基,每个数组的索引是循环长度的模:

const p = [
  [ 0 ],      // 0^1=0, 0^2=0 ...
  [ 1 ],      // 1^1=1, 1^2=1 ...
  [ 2,4,8,6 ] // 2^1=2, 2^2=4 ...
  [ 3,9,7,1 ] // 3^1=3, 3^2=9 ...
  [ 4,6 ]     
  [ 5 ]       
  [ 6 ]       
  [ 7,9,3,1 ] 
  [ 8,4,2,6 ] 
  [ 9,1 ]     
];
用法示例:
LD(3^7)==p[3][7-1%4]
-注意,我们必须从指数中减去一,因为每个数组都是基于0的

因此,我们得到了JavaScript:

LD(Math.pow(a,b)) === p[a % 10][(b-1) % p[a % 10].length]
a%10
应该是显而易见的,它只将基数的最后一位作为数组数组中的索引,因为任何非单位都不会影响最后一位

对于像问题开头的
[1,2,3]
这样的列表,可以进行递归。对于空列表,我们的初始值为1,如
x^1==x
,我们反转列表以利用
.reduce()
方法的累积:

[1,2,3].reduceRight( (a,v) => 
  p[v % 10][(a-1) % p[v % 10].length], 1)
按照如下所示进行操作,使其有意义:

  • 首先,a=1(初始值),v=3;然后
    p[3%10]==p[3]==3,9,7,1]
    ,因此
    [3,9,7,1][(1-1)%[3,9,7,1]。长度]==3,9,7,1][0%4]==3
  • 然后,
    a=3(最后一次迭代),v=2;所以
    p[2]===2,4,8,6]
    ,所以
    [2,4,8,6][2%4]==8
  • 最后,
    a=8,v=1
    <代码>p[1]====1]
    ,和
    [1][8%1]===1
因此,我们得到
LD([1,2,3])==1
,这不难验证:
1^(2^3)==1^8==1


问题:

这是可行的,只要指数不超过10,并且之后没有另一次迭代。然而,如果出现问题。让我解释一下:

假设我们有一个数组,
a=[2,2,2,2]
。由于
1
是我们的初始值,因此列表最初是
a=[1,2,2,2]
。使用上述减少值:

  • 第一次迭代
    a=1,v=2
    (记住我们的
    .reduce
    1
    作为初始值):
    • p[2%10][(1-1)%p[2%10]。长度]
    • =[2,4,8,6][0%4]
    • =2
    • 通过
      2^1=2
      很容易验证,我们的列表现在是[2,2,2,2]
  • 第二次迭代
    a=2,v=2
    • p[2%10][(2-1)%p[2%10]。长度]
    • =[2,4,8,6][1%4]
    • =4
    • 通过
      2^2=4
      很容易验证,我们的列表现在是[4,2,2]
  • 第三次迭代
    a=4,v=2
    • p[2%10][(4-1)%p[2%10]。长度]
    • =[2,4,8,6][3%4]
    • =6
    • 通过
      2^4=16
      很容易验证,我们的列表现在是[16,2]
  • 第四次迭代,问题变得明显的地方<代码>a=6,v=2:
    • p[2%10][(6-1)%p[2%10]。长度]
    • =[2,4,8,6][5%4]
    • =4
    • 很容易被
      2^16=65536
      推翻
如果你研究一段时间,你就会明白为什么。最后迭代的第三步

= [ 2,4,8,6 ][5 % 4] = p[ 2,4,8,6 ][1]
应该是

= [ 2,4,8,6 ][15 % 4] = p[ 2,4,8,6 ][3]
因此给出了一个错误的结果


问题:

基于上一个指数,是否有一种方法可以捕获通过只传递上一次迭代的最后一个数字而创建的“偏移”?我能不能在最后一次迭代中用另一条信息传递
6
,这样模数就正确了

因此,与其只是返回

p[v % 10][(a-1) % p[v % 10].length)
也许它会回来

[ 
  p[v % 10][fn(a[0], a[1]) % p[v % 10].length],
  **some other clever information here to use in the calculation to make the modulus correct**
]
其中,
fn(a[0],a[1])
使用之前的累积值以及一些其他信息来计算正确的mod值。这不一定是数组,也可能是@aec在注释中指出的对象或元组

一个(糟糕的)解决方案是跟踪累加器中的上一次迭代(例如,对于最后一步,我可以返回
16
,而不是返回
6
,并将其用于下一次迭代,这将给出正确的索引)。然而,如果数字很大,这是非常不切实际的!假设上一步的数字是
4142
623
,那么计算
4142^623
并将其传递下去是不切实际的

请注意,我知道还有其他解决方案,但我很好奇是否可以在我编写的单个
.reduce
语句中更改此代码以解决此问题。因此,是否可以通过修改以下内容来解决此问题:

array.reduceRight( (a,v) => 
  p[v % 10][(a-1) % p[v % 10].length], 1)
尽管讨论了这个问题?这几乎奏效了,我想我离成功还有一步
lastDigit
 = p[x0][(x1 ^ x2 ... ^ xn-1 ^ xn) %4]
 = p[x0][((x1 ^ x2 ... ^ xn-1)%4 ^ xn) %4]
// base 4 table
const q = [
   [0],  // 0^1=0 ...
   [1],  // 1^1=1 ...
   [2, 0, 0, 0, ..... ], // infinitely recurring
   [3, 1], // 3^1=3, 3^2=1, 3^3=3, 3^4=1 ...
];
LD2(a) | LD2(a^b)
----------------------------
0      | 1 if b = 0, else 0
1      | 1 always

LD4(a) | LD4(a^b)
----------------------------------------
0      | 1 if b = 0, else 0
1      | 1 always
2      | 1 if b = 0, 2 if b = 1, else 0
3      | 3 if b odd, else 1
function isEnd(a, i)
{
   return (i > a.length - 1);
}

function isZero(a, i)
{
   if (!isEnd(a, i))
      if (a[i] == 0)
         return !isZero(a, i+1);
   return false;
}

function isUnity(a, i)
{
   if (isEnd(a, i) || a[i] == 1)
      return true;
   return isZero(a, i+1);
}

function isOdd(a, i)
{
   if (isEnd(a, i) || a[i] % 2 == 1)
      return true;
   return isZero(a, i+1);
}

function LD2(a, i)
{
   if (isEnd(a, i) || a[i] % 2 == 1)
      return 1;
   return isZero(a, i+1) ? 1 : 0;
}

function LD4(a, i)
{
   if (isEnd(a, i)) return 1;
   switch (a[i] % 4) {
      case 0: return isZero(a, i+1) ? 1 : 0;
      case 1: return 1;
      case 2: 
         if (isZero(a, i+1)) return 1;
         if (isUnity(a, i+1)) return 2;
         return 0;
      case 3: return isOdd(a, i+1) ? 3 : 1;
      default: return -1; // exception?
   }
}

const p = [
   [ 0 ],      // 0^1=0, 0^2=0 ...
   [ 1 ],      // 1^1=1, 1^2=1 ...
   [ 2,4,8,6 ], // 2^1=2, 2^2=4 ...
   [ 3,9,7,1 ], // 3^1=3, 3^2=9 ...
   [ 4,6 ],     
   [ 5 ],      
   [ 6 ],      
   [ 7,9,3,1 ],
   [ 8,4,2,6 ],
   [ 9,1 ]
];

function LD10(a)
{
   let LDa = a[0] % 10;
   if (isZero(a, 1)) return 1; // corner case not present in tables
   const row = p[LDa];
   switch (row.length) {
      case 1: return row[0];
      case 2: return row[1 - LD2(a, 1)];
      case 4: return row[(LD4(a, 1) + 3) % 4];
      default: return -1; // exception?
   }
}
let LD = (as) =>
  as.reduceRight((acc, val) =>
    Math.pow(val < 20 ? val : (val % 20 + 20), acc < 4 ? acc : (acc % 4 + 4)) 
  , 1) % 10