Ale*_*lls 4 javascript algorithm
我有一系列代表虚拟轮播的项目.
const carousel = ['a','b','c','d','e'];
let currentIndex = 0;
function move (amount) {
const l = items.length; // in case carousel size changes
// need to update currentIndex
return carousel[currentIndex];
}
Run Code Online (Sandbox Code Playgroud)
什么是干净或聪明的方式来处理向左currentIndex == 0移动和向右移动时currentIndex == length-1?
我之前已经考虑过这个问题,从未有过任何非常聪明或简洁的事情.
// put distance in the range {-len+1, -len+2, ..., -1, 0, 1, ..., len-2, len-1}
distance = distance % len
// add an extra len to ensure `distance+len` is non-negative
new_index = (index + distance + len) % len
Run Code Online (Sandbox Code Playgroud)
使用模块化算法非常类似于您阅读典型模拟时钟的方法.前提是添加两个整数,除以整数,并保留余数.例如,13 = 3 (mod 10)因为13是1*10 + 3和3是0*10 + 3.
但为什么我们选择安排3和13我们一样呢?为了回答这个问题,我们考虑欧几里德分割算法(EDA).它说了两个整数a和b存在唯一的整数q,并r使得
a = b*q + r
Run Code Online (Sandbox Code Playgroud)
与0 ? r < b.这比你想象的更强大:它允许我们"以模数运算".
也就是说,我们可以说,a = b (mod n) 当且仅当有独特的整数q1,r1,q2,和r2这样
a = n * q1 + r1, 0 ? r1 < n
b = n * q2 + r2, 0 ? r2 < n
Run Code Online (Sandbox Code Playgroud)
和r1平等r2.我们把r1和r2的"余".
回到前面的例子,我们现在知道原因了13 = 3 (mod 10).EDA说13 = 1*10 + 3,1而且3是唯一q且r令人满意的必要限制; 通过类似的逻辑,3 = 0*10 + 3.由于余数相等,我们在"工作模式10"时说13并且3相等.
幸运的是,JavaScript 本身实现了模运算符.不幸的是,我们需要注意一个怪癖,即模运算符保持其操作数的符号.这给你一些像-6 % 5 == -1和的结果-20 % 7 == -6.虽然完全有效的数学陈述(检查原因),但在数组索引方面,这对我们没有帮助.
引理1: a + n = a (mod n)
引理2: -1 = n-1 (mod n)
引理3: -a = n-a (mod n)
克服这个问题的方法是"欺骗"JavaScript使用正确的符号.假设我们有一个长度len和当前索引的数组index; 我们想要将索引移动一段距离d:
// put `d` within the range {-len+1, -len+2, ..., -2, -1, -0}
d = d % len
// add an extra len to ensure `d+len` is non-negative
new_index = (index + d + len) % len
Run Code Online (Sandbox Code Playgroud)
我们通过首先放入d范围来实现这一目标{-len+1, -len+2, ..., -2, -1, -0}.接下来,我们添加一个额外的len以确保我们移动的距离在该范围内{1, 2, ..., len-1, len},从而确保%操作的结果具有正号.我们知道这是有效的(-a+b) + a = b (mod a).然后我们将新索引设置为index + d + len (mod len).
更详细的实施:
class Carousel {
// assumes `arr` is non-empty
constructor (arr, index = 0) {
this.arr = arr
this.index = index % arr.length
}
// `distance` is an integer (...-2, -1, 0, 1, 2, ...)
move (distance) {
let len = this.arr.length
distance = distance % len
let new_index = (this.index + distance + len) % len
this.index = new_index
return this.arr[this.index]
}
}
// usage:
let c = new Carousel(['a','b','c','d','e'], 1) // position pointer set at 'b'
c.move(-1) // returns 'a' as (1 + -1 + 5) % 5 == 5 % 5 == 0
c.move(-1) // returns 'e' as (0 + -1 + 5) % 5 == 4 % 5 == 4
c.move(21) // returns 'a' as (4 + 21 + 5) % 5 == 30 % 5 == 0
Run Code Online (Sandbox Code Playgroud)