Можно ли получить исходное значение числа после нескольких умножений ** с переполнением **?

Резюме: Есть ли способ сделать это? Вот что я имею в виду: предположим, что у меня есть unsigned int. Затем я умножаю его несколько раз (и там переполнение , которое ожидается). Тогда можно ли "вернуть" исходное значение назад?


Подробнее:

Все о Rab-Karp hash. Мне нужно сделать следующее: у меня есть хэш длинной строки - например: "abcd" . Тогда у меня есть хэш для более короткой подстроки - например, "cd". Как вычислить хэш "ab" с помощью O (1), используя две заданные хэши?

Теперь у меня есть алгоритм:

  • вычесть хеш "cd" из хэша "abcd" (удалить последние элементы из многочлена)
  • разделите хэширование "abcd" на p ^ len( "cd" ), где p - базовое (простое число).

Итак, это:

a * p ^ 3 + b * p ^ 2 + c * p ^ 1 + d * p ^ 0 - abcd

c * p ^ 1 + d * p ^ 0 - cd

ab получает:

( 
  ( a * p ^ 3 + b * p ^ 2 + c * p ^ 1 + d * p ^ 0 ) -
  ( c * p ^ 1 + d * p ^ 0 ) 
)
/ ( p ^ 2 )
= a * p ^ 1 + b * p ^ 0

И это работает, если у меня нет переполнения (если p - небольшое число). Но если это не так - это не работает.

Есть ли какой-нибудь трюк или что-то еще?

P.S. Тег c++ происходит из-за переполнения числа, поскольку он специфичен (и отличается от python, схемы или sth)

Ответ 1

Расширенный алгоритм Евклида является хорошим решением для этого, но его слишком сложно и сложно реализовать. Там лучше.


И еще один способ сделать это (спасибо моему другу (:)

Там хорошая статья в wikipedia - модульный мультипликативный обратный с использованием теоремы Эйлера в случае, когда m и a являются взаимно простыми:

Euler's theorem for coprime number and modulo

где φ(m) функция тотализатора Эйлера.

В моем случае m (по модулю) - это размер хэш-типа - 2^32, 2^64 и т.д. (в моем случае - 64 бит).
Ну, это означает, что мы должны найти только значение φ(m). Но подумайте об этом - m == 2 ^ 64 так, что дает нам гарантию, что m будет взаимно просты со всеми нечетными числами и НЕ будет взаимно простыми четным числом. Итак, нам нужно получить число всех значений и разделить их на 2.

Кроме того, мы знаем, что m будет неподписанным, так как в противном случае у нас появятся некоторые проблемы. Чем это дает нам возможность сделать это:

hash_t x = -1;
x /= 2;
hash_t a_reverse = fast_pow( a, x );

Ну, около 64-битных чисел, x действительно большое число (19 цифр: 9 223 372 036 854 775 807), но fast_pow действительно быстр, и мы можем кэшировать обратный номер, если нам нужно более одного запроса,

fast_pow - известный алгоритм:

hash_t fast_pow( hash_t source, hash_t pow )
{
    if( 0 == pow )
    {
        return 1;
    }

    if( 0 != pow % 2 )
    {
        return source * fast_pow( source, pow - 1 );
    }
    else
    {
        return fast_pow( source * source, pow / 2  );    
    }

}

Дополнение: например:

    hash_t base = 2305843009213693951;  // 9th mersenne prime
    hash_t x = 1234567890987654321;

    x *= fast_pow( base, 123456789 );   // x * ( base ^ 123456789 )

    hash_t y = -1;
    y /= 2;
    hash_t base_reverse = fast_pow( base, y );

    x *= fast_pow( base_reverse, 123456789 );   // x * ( base_reverse ^ 123456789 )
    assert( x == 1234567890987654321 ) ;

работает идеально и очень быстро.

Ответ 2

Не знаю о части переполнения, но есть способ вернуть исходное значение.

Теорема китайского останова очень помогает. Позвоните по телефону h = abcd - cd. G - это значение h, без переполнения, G = h + k*2^32, предполагая, что переполнение просто делает %2^32. И таким образом ab = G / p^2.

G = h (mod 2^32)
G = 0 (mod p^2)

Если p ^ 2 и 2 ^ 32 взаимно просты. Эта страница на Китайская теорема о остатках дает нам

G = h * b * p^2 (mod 2^32 * p^2)

Где b является модулярным мультипликативным обратным к p ^ 2 по модулю 2 ^ 32, b * p^2 = 1 (mod 2^32). После вычисления G просто разделите на p^2, чтобы найти ab.

Надеюсь, я не ошибался...

Ответ 3

Вы должны использовать целые числа без знака, чтобы получить определенное поведение переполнения (по модулю 2 ^ N). Поднятое целочисленное переполнение undefined.

Кроме того, вместо деления нужно умножить на мультипликативный обратный p по модулю соответствующего значения. Например, если p = 3, а ваши хэш-значения - 8 бит, умножьте на 171, потому что 171 * 3 = 513 = 2 * 256 + 1. Мультипликативный обратный существует, если p и по модулю значения взаимно просты.

Ответ 4

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

Но обратите внимание, что это будет иметь отдельное представление для -0 и +0, и вам, вероятно, придется вручную кодировать арифметические операции.

Некоторые из инструкций процессора являются агностиками целочисленного представления, но не все.

Ответ 5

У вас есть * b = c mod 2 ^ 32 (или модифицируйте что-то еще в зависимости от того, как вы делаете свой хэш). Если бы вы могли найти d таких, что b * d = 1 mod 2 ^ 32 (или mod any), тогда вы могли бы вычислить a * b * d = a и получить a. Если gcd (b, mod 2 ^ 32) = 1, вы можете использовать http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm, чтобы найти x и y таким образом, чтобы b * x + 2 ^ 32 * y = 1 или b * x = 1 - y * 2 ^ 32, или b * x = 1 mod 2 ^ 32, поэтому x - это число, которое вы хотите умножить на.

Ответ 6

Итак, переполнение на самом деле просто ваш компилятор хорош для вас; стандарт C/++ на самом деле предполагает, что переполнение является undefined. Поэтому, когда вы переполнены, на самом деле вы ничего не можете сделать, потому что ваша программа перестает быть детерминированной.

Вам может потребоваться переосмыслить алгоритм или прикрепить к модулю операции/вычитания, чтобы исправить ваш алгоритм.