これに対する解決策は非常に単純だと思いますが、私はしばらくの間それについて考えていて、エレガントな解決策を思い付くことができませんでした。
私は数の範囲を持っています、例えば。 1..10 = (1,2,3,4,5,6,7,8,9,10)
は循環型であり、最後の数字の後の数字が再び最初の数字になることを意味します(next(10)=1
)。
範囲内の特定の数_i>0
_について、次のm
番目と前のm
番目の数を計算したいと思います。例えばnext(5,1)=6
next(10,1)=1
next(10,2)=2
prev(5,2)=3
prev(1,1)=10
prev(1,2)=9
。
next
の場合、_(i+m)%n
_を取ることができます。ここで、n
は範囲の長さです(例では_n=10
_)。しかし、prev
の場合、エレガントな解決策を見つけることができませんでした。
1を引いて、後で1を足すだけです。
ほとんどのプログラミング言語では、「前の」値を見つけるときに注意する必要があります。負の数の場合、この場合、モジュロは期待どおりに機能しないためです。負の数を返します。
C/C++バージョンは次のとおりです。
int next(int i, int m, int n) { return (i + m - 1) % n + 1; }
int prev(int i, int m, int n) { return (i - m + n - 1) % n + 1; }
ただし、Perlでは、モジュロは常に正の値を返します(少なくとも、第2オペランドが正の整数の場合)。基本的にそれはあなたが望むことをします。したがって、次のように記述して、+ $_[2]
を省略できます。
sub nxt { ($_[0] + $_[1] - 1) % $_[2] + 1; }
sub prv { ($_[0] - $_[1] - 1) % $_[2] + 1; }
とにかくあなたのnext = (i + m) % n
は正しくありません-場合によってはゼロを返します。
代わりにこれを試してください:
next(i, m) = ((i - 1) + m) % n + 1
prev(i, m) = ((i - 1) + n - m) % n + 1
実際には、1つを外してから正しい値を見つけてから、もう一度1つ追加します。
prev
の場合、最初にn
を追加して、負の数のモジュロをとらないようにします。
next(i,m)
とprevious(i,-m)
の違いは何ですか?何も!じゃあ行こう (i - 1 + n + m % n) % n + 1
:
$ Perl -le 'sub gen {my $n = shift; return sub{ my ($i, $m) = @_; return ($i - 1 + $n + $m % $n) % $n + 1;};} $"=","; for my $n (2..5) { my $f = gen($n); print "$n: @{[map {$f->(1,$_)} -10 .. 10]}"}'
2: 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1
3: 3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2
4: 3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3
5: 1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1
Tie :: Cycle のソースを見ると、任意のリストを循環するために作成したモジュールです。
数字は実際には何かを表す単なるグリフであることを忘れないでください。これらのグリフのPerlリストがある場合でも、グリフではなくリストインデックスで計算を行うため、ゼロから始まるシーケンスがあります。適切なリストインデックスを選択したら、そのインデックスの要素を使用します。
非常に大きなリストや怠惰なリストが必要な場合でも、これを行うことができますが、もう少し作業を行う必要があります。
あなたが気にしないのであれば、一般的に最初にいくつかの言葉。
「prev」関数を実装する際の混乱は、正と負の整数のドメインでこの問題について考えることから生じます。ジオメトリの観点から考えてみてください。10個の等間隔の点を持つ円を視覚化した場合、ソリューションは次のようになります。
正しく指定したように、範囲_[x..z]
_が与えられ、範囲が循環している場合、次の_m-th number
_は_(i+m)%k where i belongs to [x..z]
_であり、k
は範囲の長さです。
ここで、「前の」m番目のメンバーの場合。前の番号は、次のように前のm番目の番号の位置を計算(またはより視覚的に表現すると、「到着」)することで見つけることができます(擬似コード) :
prev(m, i) = (i + len(range) - m) % len(range)
たとえば、前の10番をとると、
_prev(1,10) = (10+10-1)%10 = 19%10 = 9
_
番号5の前の3番目= prev(3,5) = (5+10-3)%10 = 12%10 = 2
。 Etceteraなど。 とてもシンプルでエレガントですね
ここでの唯一の注意点は、_if i == m
_、モジュロがゼロになることです。したがって、next()関数とprev()関数の両方でこの結果を処理するメカニズムが必要です。
これがお役に立てば幸いです、ジャス。
私はRでこの解決策を持っています:
pred <- function(n) n - 1L # cf. Pascal's pred
succ <- function(n) n + 1L # cf. Pascal's succ
`%mod1%` <- function(m, n) succ(pred(m) %% n) # modulo from 1
cat(-11:24 %mod1% 12) # test
# 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12