Уменьшите этот цикл до уравнения

Эта функция (написана на C для удобства, но это не важно для вопроса) определяет размер массива. Я уверен, что это можно преобразовать в цепочку if-else или даже в уравнение, но я не достаточно умен, чтобы понять, как это сделать. (Я попытался записать очевидную цепочку if-else, но застрял в делах.)

// 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;
}

Если есть общий, идиотостойкий метод для преобразования этого вида цикла в арифметику, это был бы идеальный ответ. В противном случае, решение для этого экземпляра будет в порядке.

2 ответа

Решение

Объединив ответ nmclean и концепцию из этого вопроса, я получил следующее:

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);

который я тщательно проверил, чтобы всегда давать те же результаты, что и оригинал, и который ясно показывает, как делать подобные вещи в общем случае.

Во-первых, для простоты:

to = from + len - 1

Я думаю, что это может быть разбито на 3 уравнения, для каждого "раздела". То есть:

  • A: 0 в 0xD800 - 1
  • B: 0xD800 в 0xDFFF
  • C: 0xDFFF + 1 до бесконечности

Раздел A и C "стоят" 2, а B стоит 1. Если я неправильно истолковал ваш код - есть только 2 раздела?

Таким образом, умножьте каждое значение секции на длину диапазона, который попадает в него:

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

Если предположить, min это функция, которая возвращает меньшее из своих аргументов: диапазон " from до конца раздела, или len "Чем меньше". Диапазон (конец - начало + 1).

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

Логика здесь аналогична: "полный раздел или начало раздела к to короче ".

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

Это проще, потому что нет конечной точки: просто считать от начала до to,

Я понятия не имею, будет ли это более эффективным для компьютера. Хотя это определенно менее эффективно для моего мозга.

Другие вопросы по тегам