Reduce this loop to an equation

89 Views Asked by At

This function (written in C for convenience, but this is not important to the question) determines the size of an array. I'm sure it can be converted to an if-else chain or even to an equation, but I am not clever enough to see how. (I tried to write down the obvious if-else chain but got bogged down in cases.)

// 0 <= from <= 0x10FFFF
// 1 <= len <= 0x10FFFF
unsigned int size_for_block(unsigned int from, unsigned int len)
{
  unsigned int size = 0;
  for (unsigned int i = 0; i < len; i++) {
    unsigned int point = from + i;
    if (0xD800 <= point && point <= 0xDFFF)
      ;
    else if (point <= 0xFFFF)
      size += 1;
    else
      size += 2;
  }
  return size;
}

If there is a general, idiotproof technique for converting this kind of loop to arithmetic, that would be an ideal answer. Failing that, the solution for this instance would be fine.

2

There are 2 best solutions below

0
On BEST ANSWER

By combining nmclean's answer and the concept from this question, I now have this:

function overlap(min1, max1, min2, max2) {
  return Math.max(0, Math.min(max1, max2) - Math.max(min1, min2));
}
size = (overlap(from, from+len, 0x000000, 0x00D800) +
        overlap(from, from+len, 0x00E000, 0x010000) +
        overlap(from, from+len, 0x010000, 0x110000)*2);

which I have exhaustively tested to always produce the same results as the original, and which clearly shows how to do this sort of thing in the general case.

1
On

First, for simplicity:

to = from + len - 1

I think it could be broken into 3 equations, for each "section". That is:

  • A: 0 to 0xD800 - 1
  • B: 0xD800 to 0xDFFF
  • C: 0xDFFF + 1 to infinity

Section A and C are "worth" 2, and B is worth 1. Unless I have misinterpreted your code -- is there only 2 sections?

So multiply each section value by the length of the range that falls within it:

A: if (from < 0xD800) size += 2 * min((0xD800 - 1) - from + 1, len)

Assuming min is a function that returns the lesser of its arguments: The range is "from to the end of the section, or len, whichever is shorter". Range is (end - start + 1).

B: if (to > 0xD800) size += 1 * min(0xDFFF - 0xD800 + 1, to - D800 + 1)

The logic is similar here: "the full section, or the beginning of the section to to, whichever is shorter".

C: if (to > 0xDFFF + 1) size += 2 * (to - (0xDFFF + 1) + 1)

This is simpler because there is no end point: just count from the beginning to to.

I have no idea if this would be more efficient for a computer. It's definitely less efficient for my brain, though.