2013-02-09 18 views
10

Đối với bất kỳ số nguyên đầu vào W giới hạn bởi phạm vi R = [x, y], các "tràn", vì thiếu một thuật ngữ tốt hơn, trong W qua RW % (y-x+1) + x . Điều này làm cho nó quấn lại xung quanh nếu W vượt quá y.Có một biểu thức bằng cách sử dụng modulo để làm backwards wrap-around ("reverse overflow")?

Như một ví dụ về nguyên tắc này, giả sử chúng ta lặp qua tháng của lịch:

int this_month = 5; 
int next_month = (this_month + 1) % 12; 

nơi cả hai số nguyên sẽ là từ 0 đến 11, bao gồm. Do đó, biểu thức ở trên "kẹp" số nguyên vào phạm vi R = [0,11]. Cách tiếp cận sử dụng biểu thức này đơn giản, thanh lịch và thuận lợi vì nó bỏ phân nhánh.

Bây giờ, nếu chúng ta muốn làm điều tương tự, nhưng ngược lại thì sao? Biểu thức sau đây hoạt động:

int last_month = ((this_month - 1) % 12 + 12) % 12; 

nhưng không thể hiểu được. Làm thế nào nó có thể được làm đẹp?


tl; dr - Có thể biểu hiện ((x-1) % k + k) % k được đơn giản hóa hơn nữa?

Lưu ý: Thẻ C++ được chỉ định vì các ngôn ngữ khác xử lý toán hạng âm cho toán tử modulo khác nhau.

Trả lời

9

biểu thức phải là ((x-1) + k) % k. Nói chung, nếu bạn muốn lùi lại nhiều hơn 1, bạn cần đảm bảo rằng bạn thêm đủ để toán hạng đầu tiên của phép toán modulo là> = 0.

Đây là một triển khai trong Python.Bạn sẽ có thể chuyển nó sang ngôn ngữ của sự lựa chọn của bạn:

def wrap_around(value, delta, min_val, max_val): 
    if delta >= 0: 
     return (value + delta - min_val) % max_val + min_val 
    else: 
     return ((value + delta) + max_val * (-delta) - min_val) % max_val + min_val 

này cũng cho phép sử dụng tháng dán nhãn 0-11 hoặc 1-12, thiết min_valmax_val cho phù hợp.

+3

'((x-1) + k)% k' là giải pháp! – CpILL

+0

Số '-1' không thể thấp hơn thì' - (k-1) ' –

7

k% k sẽ luôn bằng 0. Tôi không chắc chắn 100% những gì bạn đang cố gắng làm nhưng có vẻ như bạn muốn tháng cuối được kẹp từ 0 đến 11.

(this_month + 11) % 12 

Nếu đủ.

+3

Thực ra, '-1% 12 == -1'. – Mankarse

+0

'-1% 12 = 11' - là nó trong' C++'? –

+0

@ Mankarse22 hiện nó? Tôi chỉ thử nó trong máy tính của tôi, đã không nhận được một cơ hội để thử nó trong một trình biên dịch. – Enfyve

4

Các giải pháp chung là viết một hàm tính giá trị mà bạn muốn:

//Returns floor(a/n) (with the division done exactly). 
//Let ÷ be mathematical division, and/be C++ division. 
//We know 
// a÷b = a/b + f (f is the remainder, not all 
//     divisions have exact Integral results) 
//and 
// (a/b)*b + a%b == a (from the standard). 
//Together, these imply (through algebraic manipulation): 
// sign(f) == sign(a%b)*sign(b) 
//We want the remainder (f) to always be >=0 (by definition of flooredDivision), 
//so when sign(f) < 0, we subtract 1 from a/n to make f > 0. 
template<typename Integral> 
Integral flooredDivision(Integral a, Integral n) { 
    Integral q(a/n); 
    if ((a%n < 0 && n > 0) || (a%n > 0 && n < 0)) --q; 
    return q; 
} 

//flooredModulo: Modulo function for use in the construction 
//looping topologies. The result will always be between 0 and the 
//denominator, and will loop in a natural fashion (rather than swapping 
//the looping direction over the zero point (as in C++11), 
//or being unspecified (as in earlier C++)). 
//Returns x such that: 
// 
//Real a = Real(numerator) 
//Real n = Real(denominator) 
//Real r = a - n*floor(n/d) 
//x = Integral(r) 
template<typename Integral> 
Integral flooredModulo(Integral a, Integral n) { 
    return a - n * flooredDivision(a, n); 
} 
1

Không chắc nếu bạn đang gặp vấn đề tương tự như tôi, nhưng vấn đề của tôi là về cơ bản mà tôi muốn hạn chế tất cả số đến một phạm vi nhất định. Nói rằng phạm vi đó là 0-6, do đó, việc sử dụng% 7 có nghĩa là bất kỳ số nào cao hơn 6 sẽ quấn ngược lại thành 0 hoặc cao hơn. Vấn đề thực tế là số ít hơn 0 không quấn lại khoảng 6. Tôi có giải pháp cho điều đó (trong đó X là giới hạn trên của phạm vi số của bạn và 0 là tối thiểu):

if(inputNumber <0)//If this is a negative number 
{ 
(X-(inputNumber*-1))%X; 
} 
else 
{ 
inputNumber%X; 
} 
2

Dễ dàng Peasy, không sử dụng các nhà điều hành mô-đun đầu tiên, đó là không cần thiết:

int last_month = (this_month - 1 + 12) % 12; 

đó là trường hợp chung

Trong trường hợp này bạn có thể viết 11, nhưng tôi vẫn sẽ làm -1 + 11 vì nó nêu rõ hơn những gì bạn muốn đạt được.

Các vấn đề liên quan