Modulo algorithm proving elusive

72 views Asked by At

I have a color-wheel that maps a color to each hour on a 24-hour clock. Now given the hour of day, I want to map those colors to a 12-hour clock such that the colors 5 hours before and 6 hours after the current hour are used. But it gets a bit tricky b/c the 0th index of the result always has to be the 0th color or the 12th color of the 24 color-wheel.

For example, given colors24 as an array of 24 colors and a hour time of 5 then the final color12 array would map to colors24's indexes as:

{0,1,2,3,4,5,6,7,8,9,10,11}

If the hour is 3, then:

{0,1,2,3,4,5,6,7,8,9,22,23}

And if the hour is 9, then:

{12,13,14,15,4,5,6,7,8,9,10,11}

Bonus points if the algorithm can be generalized to any two arrays regardless of size so long as the first is evenly divisible by the second.

4

There are 4 answers

2
trincot On BEST ANSWER

If hours is the total number of hours (24), length the number of colors displayed at a time (12), and hour is the current hour, then this is a generic algorithm to get the indexes into the color array:

result = [];
add = hour + hours - (length / 2) - (length % 2) + 1;
for (i = 0; i < length; i++) {
    result[(add + i) % length] = (add + i) % hours;
}

Here is a Javascript implementation (generic, can be used with other ranges than 24/12):

function getColorIndexes(hour, hours, length) {
    var i, result, add;

    if (hours % length) throw "number of hours must be multiple of length";
    result = [];
    add = hour + hours - (length / 2) - (length % 2) + 1;
    for (i = 0; i < length; i++) {
        result[(add + i) % length] = (add + i) % hours;
    }
    return result;
}

console.log ('hour=3: ' + getColorIndexes(3, 24, 12));
console.log ('hour=5: ' + getColorIndexes(5, 24, 12));
console.log ('hour=9: ' + getColorIndexes(9, 24, 12));
console.log ('hour=23: ' + getColorIndexes(23, 24, 12));

As stated in the question, the number of hours (24) must be a multiple of the length of the array to return.

0
LSerni On

Can you not get the colors straight away, i.e. from (C-Y/2+X+1)%X to (C+Y/2)%X, and then sort them?

(This is the same as looping (C+Z+X+1)%X from Z = -Y/2 to Z = Y/2-1):

for (i = 0, j = c+x+1, z = -y/2; z < y/2; z++) {
    color[i++] = (z+j)%x;
}

For C=3, X=24 and Y=12, you get:

 (C-12/2+24+1)%24 = 3-6+24+1 = 22, 23, 0, 1 .. 9

After sorting you get 0, 1 ...9, 22, 23 as requested.

Without sorting, you'd always get a sequence with the current hour smack in the middle (which could be good for some applications), while your 3 example has it shifted left two places.

You can do this by shifting instead of sorting by noticing that you only need to shift if c is below Y/2 (C=3 makes you start from -2, which becomes 22), in which case you shift by negative y/2-c (here, 2, or 12+2 using another modulus), or if c > (x-y/2), in which case you'd end beyond x: if c = 20, c+6 is 26, which gets rolled back to 2:

15 16 17 18 19 20 21 22 23 0 1 2

and gives a s factor of 2+1 = 3, or (c+y/2)%x+1 in general:

0 1 2 15 16 17 18 19 20 21 22 23


for (i = 0, j = c+x+1, z = -y/2; z < y/2; z++) {
    color[(s+i++)%y] = (z+j)%x;
}

However, I think you've got a problem if x > 2*y; in that case you get some c values for which neither 0, nor x/2 are "in reach" of c. That is, "evenly divisible" must then mean that x must always be equal to y*2.

1
Sergey Kalinichenko On

This can be done by first placing the numbers into a temporary array, then finding the location of 0 or 12 in it, and printing the results from that position on, treating the index as circular (i.e. modulo the array length)

Here is an example implementation:

int num[12];
// Populate the values that we are going to need
for (int i = 0 ; i != 12 ; i++) {
    // 19 is 24-5
    num[i] = (h+i+19) % 24;
}
int p = 0;
// Find p, the position of 0 or 12
while (num[p] != 0 && num[p] != 12) {
    p++;
}
// Print num[] array with offset of p
for (int i = 0 ; i != 12 ; i++) {
    printf("%d ", num[(p+i) % 12]);
}

Demo.

Note: The first and the second loops can be combined. Add a check if the number you just set is zero or 12, and set the value of p when you find a match.

2
Matt On

Here is a solution in JavaScript:

function f(h) {
  var retval = [];
  for (var i = h - 5; i <= h + 6; ++i)
    retval.push((i+24) % 24);
  return retval.sort(function(a,b){return a-b;}); // This is just a regular sort
}

https://repl.it/CWQf

For example,

f(5) // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
f(3) // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 22, 23 ]
f(9) // [ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]