Контрольная сумма больших полос простых чисел? (для подтверждения)

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

Мотивация:

Небольшие простые числа — размером до 64 бит — могут быть просеяны по запросу с точностью до миллионов в секунду с использованием небольшого битового массива для просеивания потенциальных множителей (до 2^32-1) и второго битового массива для просеивания чисел в целевой диапазон.

Алгоритм и реализация достаточно просты и прямолинейны, но дьявол кроется в деталях: значения, как правило, выходят за пределы встроенных целочисленных типов или превышают их повсюду, граничных случаев предостаточно (так сказать), и даже различия в строгости с плавающей запятой могут вызвать поломка, если программирование не является защитным. Не говоря уже о хаосе, который может устроить оптимизирующий компилятор даже с уже скомпилированным, уже протестированным кодом в статической библиотеке (если используется генерация кода во время компоновки). Не говоря уже о том, что более быстрые алгоритмы, как правило, намного сложнее и, следовательно, еще более хрупкие.

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

Проверка по предварительно вычисленным значениям даст наивысшую степень уверенности, но требуемые файлы большие и неуклюжие. Текстовый файл с 10 миллионами простых чисел имеет порядка 100 МБ в несжатом виде и более 10 МБ в сжатом виде; для хранения различий в байтовом кодировании требуется один байт на одно простое число, а энтропийное кодирование может в лучшем случае уменьшить размер вдвое (5 МБ для 10 миллионов простых чисел). Следовательно, даже файл, который охватывает только небольшие множители до 2^32, будет весить около 100 МБ, а сложность декодера превысит сложность оконного сита.

Это означает, что проверка файлов невозможна, за исключением проверки окончательного выпуска для вновь созданного исполняемого файла. Не говоря уже о том, что надежные файлы найти непросто. Prime Pages предлагают файлы для первых 50 миллионов простых чисел и даже потрясающие primos.mat.br доходит только до 1 000 000 000 000. Это печально, так как многие пограничные случаи (== необходимость тестирования) происходят между 2^62 и 2^64-1.

Это оставляет контрольную сумму. Таким образом, требования к пространству будут минимальными и пропорциональны только количеству тестовых случаев. Я не хочу требовать, чтобы была доступна приличная контрольная сумма, такая как MD5 или SHA-256, и с учетом того, что все целевые числа являются простыми, должна быть возможность создать высококачественную контрольную сумму с высоким разрешением с помощью некоторых простых операций над числами. самих себя.

Это то, что я придумал до сих пор. Необработанный дайджест состоит из четырех 64-битных чисел; в конце его можно сложить до нужного размера.

   for (unsigned i = 0; i < ELEMENTS(primes); ++i)
   {
      digest[0] *= primes[i];              // running product (must be initialised to 1)
      digest[1] += digest[0];              // sum of sequence of running products
      digest[2] += primes[i];              // running sum
      digest[3] += digest[2] * primes[i];  // Hornerish sum
   }

При двух (независимых) мультах на прайм скорость вполне приличная, и за исключением простой суммы каждый из компонентов всегда выявлял все ошибки, которые я пытался прокрасться мимо дайджеста. Однако я не математик, и эмпирическое тестирование не является гарантией эффективности.

Существуют ли какие-то математические свойства, которые можно использовать для разработки (а не «приготовления», как это сделал я) разумной и надежной контрольной суммы?

Можно ли спроектировать контрольную сумму таким образом, чтобы сделать ее пошаговой, в том смысле, что поддиапазоны могут обрабатываться отдельно, а затем результаты объединяются с небольшим количеством арифметических операций, чтобы получить тот же результат, как если бы весь диапазон был просчитан за один раз? ? То же самое, что и все современные реализации CRC, чтобы обеспечить параллельную обработку.

РЕДАКТИРОВАТЬ Обоснование текущей схемы таково: количество, сумма и произведение не зависят от порядка, в котором простые числа добавляются к дайджесту; они могут быть вычислены в отдельных блоках, а затем объединены. Контрольная сумма зависит от порядка; это его смысл существования. Однако было бы неплохо, если бы две контрольные суммы двух последовательных блоков можно было каким-то образом объединить, чтобы получить контрольную сумму объединенного блока.

Количество и сумму иногда можно проверить по внешним источникам, таким как определенные последовательности на oeis.org, или по таким источникам, как пакеты из 10 миллионов простых чисел на primos.mat.br (индекс дает первое и последнее простое число, число = = 10 миллионов подразумевается). Однако не повезло с продуктом и контрольной суммой.

Прежде чем я потрачу много времени и вычислительных мощностей на вычисление и проверку дайджестов, охватывающих весь диапазон малых коэффициентов до 2^64, я хотел бы услышать, что об этом думают эксперты...

Схема, которую я сейчас тестирую в 32-битном и 64-битном вариантах, выглядит так:

template<typename word_t>
struct digest_t
{
   word_t count;
   word_t sum;
   word_t product;
   word_t checksum;

   // ...

   void add_prime (word_t n)
   {
      count    += 1;
      sum      += n;
      product  *= n;
      checksum += n * sum + product;
   }
};

Преимущество этого заключается в том, что 32-битные компоненты дайджеста равны младшим половинам соответствующих 64-битных значений, а это означает, что для сохранения необходимо вычислять только 64-битные дайджесты, даже если требуется быстрая 32-битная проверка. 32-разрядную версию дайджеста можно найти в этой простой программе ситового тестирования @ pastebin для практических экспериментов. . Полный Монти в исправленной, шаблонной версии можно найти в более новой пасте для решета, которое работает до 2^64-1< /а>.


person DarthGizka    schedule 28.10.2014    source источник


Ответы (3)


Я проделал большую работу по распараллеливанию операций на архитектурах Cell. У этого похожее ощущение.

В этом случае я бы использовал быструю и, возможно, инкрементальную хэш-функцию (например, xxHash или < href="https://code.google.com/p/smhasher/wiki/MurmurHash3" rel="nofollow">MurmurHash3) и список хешей (это менее гибкая специализация Дерево Меркла).

Эти хэши очень быстрые. Будет на удивление сложно добиться улучшения с помощью какого-то простого набора операций. Список хэшей обеспечивает параллелизм — разные блоки списка могут обрабатываться разными потоками, а затем вы хешируете хэши. Вы также можете использовать дерево Меркла, но я подозреваю, что это будет более сложно без особой пользы.

  • Виртуально разделите свой диапазон на выровненные блоки — мы назовем их микроблоками. (например, микроблок — это диапазон, такой как [n‹‹15, (n+1)‹‹15))
  • Чтобы обработать микроблок, вычислите то, что вам нужно вычислить, добавьте его в буфер, хешируйте буфер. (Инкрементная хэш-функция позволит уменьшить размер буфера. Буфер не обязательно каждый раз заполнять данными одинаковой длины.)
  • Хэш каждого микроблока будет помещен в циклический буфер.
  • Разделите кольцевой буфер на хешируемые блоки («макроблоки»). Постепенно хешируйте эти макроблоки в правильном порядке по мере их появления или если микроблоков больше не осталось.
  • Полученный хеш — тот, который вам нужен.

Некоторые дополнительные примечания:

  • Я рекомендую схему, в которой потоки резервируют диапазон ожидающих микроблоков, для которых есть место в кольцевом буфере, обрабатывают их, выгружают значения в кольцевой буфер и повторяют.
  • Это имеет дополнительное преимущество, заключающееся в том, что вы можете решить, сколько потоков вы хотите использовать на лету. например при запросе нового диапазона микроблоков каждый поток может определить, слишком ли много/мало запущенных потоков, и скорректировать.
  • Лично я хотел бы, чтобы поток, добавляющий хэш последнего микроблока к макроблоку, очищал этот макроблок. Меньше параметров для настройки таким образом.
  • Поддерживать циклический буфер не так сложно, как кажется — макроблок самого низкого порядка, который еще не обработан, определяет, какую часть «пространства макроблока» представляет циклический буфер. Все, что вам нужно, это простой счетчик, который увеличивается, когда это необходимо, чтобы выразить это.
  • Еще одно преимущество заключается в том, что поскольку потоки регулярно проходят цикл резервирования/работы/резервирования/работы, поток, который неожиданно замедляется, не будет так сильно мешать времени выполнения.
  • Если вы хотите сделать что-то менее надежное, но более простое, вы можете отказаться от значительной части работы, используя «полосатый» шаблон — определите максимальное количество потоков (N) и пусть каждый поток обрабатывает каждый N- й микроблок (со смещением по его потоку «ID») и вместо этого хэшировать полученные макроблоки для каждого потока. Затем, в конце, хешируйте хэши макроблоков из N потоков. Если у вас меньше N потоков, вы можете разделить работу между нужным вам количеством потоков. (например, 64 максимальных потока, но три реальных потока, поток 0 обрабатывает 21 виртуальный поток, поток 1 обрабатывает 21 виртуальный поток, а поток 2 обрабатывает 22 виртуальных потока — не идеально, но не ужасно). По сути, это мелкое дерево Меркеля вместо список хешей.
person Kaganar    schedule 11.11.2014
comment
По общему признанию, я в основном проигнорировал ваш запрос на хеш-функцию, которая имеет какие-то более сильные свойства, чем обычные функции дайджеста, и обменял это на то, как вы можете сделать любую дайджест-функцию по вашему выбору распараллеливаемой. Но при этом вы, скорее всего, отбросите все интересные свойства хеш-функции с более сильными свойствами. - person Kaganar; 11.11.2014
comment
Ваш ответ очень интересен и хорошо продуман. Это не совсем решает проблему, как указано, поскольку требует сложной хеш-функции, сложность которой порядка MD5 или SHA-256, и не использует специальные свойства ввода (все простые числа). Ваше решение, однако, превосходно решает немного более общую проблему, а именно хэширование больших участков памяти разделяемым (параллизуемым/пошаговым) способом, даже если базовая хеш-функция не является пошаговой (в отличие от CRC, где вы можете использовать линейную алгебру для сшить результаты для последовательных блоков вместе) - person DarthGizka; 12.11.2014
comment
Следовательно, я присуждаю награду за ваш отличный ответ, а также с учетом того факта, что эксперты, привлеченные сюда вопросом темы, могут ожидать найти что-то подобное. Я сам довольно люблю Мурмурхаша, и деревья Меркле несколько раз спасали мою задницу. Деревья Меркла естественным образом подходят для решения этой задачи в том смысле, что одно из предполагаемых применений дайджеста состоит в том, чтобы покрыть диапазон 0..2^64-1 примерно как B-дерево, добавляя результаты по мере их появления и проверено. Цель состоит в том, чтобы разрешить последующую проверку без хранения терабайтов данных. - person DarthGizka; 12.11.2014

Отличный ответ Каганара демонстрирует, как заставить все работать, даже если дайджесты для соседних блоков не могут быть объединены математически, чтобы дать тот же результат, как если бы вместо этого был обработан объединенный блок.

Единственным недостатком его решения является то, что результирующая блочная структура по необходимости является довольно жесткой, похожей на PKI. с его официальной всеохватывающей иерархией сертификатов по сравнению с PGP «партизанского стиля», чья сеть доверия охватывает только несколько тем, которые представляют интерес. Другими словами, это требует разработки глобальной структуры/иерархии адресации.

Это дайджест в его нынешнем виде; изменение заключается в том, что часть, зависящая от порядка, была упрощена до необходимого минимума:

void add_prime (word_t n)
{
   count    += 1;
   sum      += n;
   product  *= n;
   checksum += n * count;
}

Вот уроки, извлеченные из практической работы с этим дайджестом:

  • count, sum и product (то есть частичный первоначальный размер слова по модулю) оказались чрезвычайно полезными из-за того, что они относятся к вещам, которые также можно найти в других местах в мире, например, некоторые списки в OEIS.
  • count и sum были очень полезны, потому что первый, как правило, естественно доступен при манипулировании (генерировании, использовании, сравнении) пакетами простых чисел, а сумма легко вычисляется на лету без каких-либо усилий; это позволяет выполнять частичную проверку существующих результатов, не прибегая к созданию и обновлению экземпляра дайджеста, а также без накладных расходов на два — сравнительно медленных — умножения.
  • count также чрезвычайно полезен, поскольку он по необходимости должен быть частью любой надстройки индексации, построенной на системах дайджестов, и, наоборот, он может направлять поиск прямо к блоку (диапазону), содержащему n-е простое число, или к блокам, перекрывающимся от n-го до (n+k)-е простые числа
  • зависимость четвертого компонента от порядка (checksum) оказалась меньшей помехой, чем предполагалось, поскольку небольшие простые числа имеют тенденцию «возникать» (генерироваться или использоваться) по порядку в ситуациях, когда может потребоваться проверка
  • зависимость контрольной суммы от порядка - и отсутствие возможности комбинирования - сделали ее совершенно бесполезной за пределами конкретного блока, для которого она была сгенерирована.
  • вспомогательные программные структуры фиксированного размера — такие как вездесущие растровые изображения с малым коэффициентом — лучше всего проверять как необработанную память для самопроверок при запуске, вместо того, чтобы запускать для них дайджест простых чисел; это резко снижает сложность и ускоряет работу на несколько порядков

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

Для проверки фиксированных диапазонов (например, в самотестировании) компонент контрольной суммы по-прежнему полезен. Любой другой тип контрольной суммы — моральный эквивалент CRC — был бы так же полезен для этого и, вероятно, быстрее. Было бы еще полезнее, если бы можно было найти независимый от порядка (комбинируемый) способ дополнения разрешения первых трех компонентов. Расширение разрешения за пределы первых трех компонентов наиболее актуально для более крупных вычислений, таких как просеивание, проверка и обработка триллионов простых чисел для потомков.

Одним из таких кандидатов на независимую от порядка комбинируемую четвертую компоненту является сумма квадратов.

В целом дайджест и так оказался весьма полезным, несмотря на недостатки, связанные с компонентом контрольной суммы. Лучший способ взглянуть на дайджест, вероятно, состоит из «характеристической» части (первые три компонента, которые можно комбинировать) и части контрольной суммы, которая относится только к конкретному блоку. Последний с таким же успехом можно заменить хешем любого желаемого разрешения. Решение Каганара показывает, как эту контрольную сумму/хеш можно интегрировать в систему, выходящую за рамки одного блока, несмотря на присущую ей несовместимость.

Сводка источников простых чисел, похоже, отошла на второй план, так что вот она:

  • до 1 000 000 000 000 доступны в виде файлов с таких сайтов, как primos.mat.br
  • до 2^64-10*2^64 сверхбыстрым пакетом через консольную программу primesieve.org (pipe)
  • до 2^64-1 — и выше — через программу gp/PARI (pipe, около 1 миллиона простых чисел в минуту)
person DarthGizka    schedule 12.11.2014
comment
Спасибо за документирование вашего анализа - я нашел его интересным для чтения. - person Kaganar; 13.11.2014

Я снова отвечаю на этот вопрос во втором ответе, так как это совсем другой и, надеюсь, лучший подход:

Мне пришло в голову, что то, что вы делаете, в основном ищет контрольную сумму не по списку простых чисел, а по диапазону битового поля, где число является простым (бит установлен на 1) или нет (бит установлен до 0). У вас будет намного больше нулей, чем единиц для любого интересного диапазона, так что, надеюсь, вам нужно выполнить операцию только для единиц.

Обычно проблема с использованием тривиального хэша в любом порядке заключается в том, что они плохо обрабатывают множественность и не обращают внимания на порядок. Но вас не волнует ни одна из этих проблем — каждый бит может быть установлен или снят только один раз.

С этой точки зрения, побитовое исключающее ИЛИ или сложение должно быть просто прекрасным в сочетании с хорошей функцией хеширования индекса бита, то есть найденного простого числа. (Если ваши простые числа 64-разрядные, вы можете воспользоваться некоторыми функциями здесь.)

Итак, для максимальной простоты, которая даст вам одно и то же значение для любого набора диапазонов входных данных, да, придерживайтесь хеширования и комбинируйте его с простой операцией, как вы. Но переход на традиционную хеш-функцию, которая выглядит "случайно" с учетом ее ввода -- hash64shift на связанной странице, скорее всего, то, что вы ищете. Вероятность осмысленного столкновения мала. Однако большинство хэш-функций воняет — убедитесь, что вы выбрали ту, которая, как известно, обладает хорошими свойствами. (Лавины хорошо, и т.д.) Томаса Ванга, как правило, не так уж плохо. (У Боба Дженкина просто фантастика, но он в основном придерживается 32-битных функций. Хотя его функция микширования на связанной странице очень хороша, но, вероятно, избыточна.)

Распараллеливание проверки, очевидно, тривиально, размер кода и усилия значительно сокращаются по сравнению с моим другим ответом, и требуется гораздо меньше синхронизации и почти не требуется буферизация.

person Kaganar    schedule 11.11.2014
comment
Наиболее естественной формой ввода является последовательность простых чисел, поскольку фактическое хранилище может сильно различаться. Векторы, списки, упакованные растровые изображения составных или простых чисел (т. е. те же самые, но инвертированные), работающие байтовые различия (быстрее и компактнее, чем растровые изображения) и даже более компактные формы с использованием колес, таких как знаменитое колесо mod 30, которое эффективно вставляет 30 чисел в один байт. Что касается лавины: хеш-миксер бормотания умопомрачительно хорош. Это (обманчиво) просто, элегантно и невероятно эффективно. Вещь красоты. - person DarthGizka; 12.11.2014
comment
И да, Боб Дженкинс заслуживает медали за свою работу. Это обязательное чтение для всех, кто работает в этой области, и источник вдохновения. - person DarthGizka; 12.11.2014