Подтвердить что ты не робот

Сопоставление двух целых чисел с единым, уникальным и детерминированным образом

Представьте себе два положительных целых числа A и B. Я хочу объединить эти два в одно целое C.

Не может быть никаких других целых чисел D и E, которые объединяются с C. Поэтому объединение их с оператором добавления не работает. Например, 30 + 10 = 40 = 40 + 0 = 39 + 1 Работа не выполняется. Например, "31" + "2" = 312 = "3" + "12"

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

4b9b3361

Ответ 1

Вы ищете биективное отображение NxN -> N. Они используются, например. dovetailing. Посмотрите этот PDF для введения в так называемые функции сопряжения. Википедия вводит определенную функцию спаривания, а именно функцию Cantor pairing:

pi(k1, k2) = 1/2(k1 + k2)(k1 + k2 + 1) + k2

Три замечания:

  • Как ясно из других, если вы планируете реализовать функцию сопряжения, вы можете вскоре найти вам произвольно большие целые числа (bignums).
  • Если вы не хотите проводить различие между парами (a, b) и (b, a), затем выполните сортировку a и b перед применением спаривания.
  • Вообще-то я соврал. Вы ищете биективное отображение ZxZ -> N. Функция Кантора работает только с неотрицательными числами. Однако это не проблема, потому что легко определить биекцию f : Z -> N, например:
    • f (n) = n * 2, если n >= 0
    • f (n) = -n * 2 - 1, если n < 0

Ответ 2

Функция связывания Кантора действительно является одной из лучших из них, учитывая ее простую, быструю и экономичную площадь, но есть что-то еще лучше опубликованное на Wolfram от Матфея Szudzik, здесь. Ограничение функции связывания Cantor (относительно) заключается в том, что диапазон закодированных результатов не всегда остается в пределах целочисленного целого числа 2N, если входы представляют собой два целых числа N. То есть, если мои входы представляют собой два целых числа 16 в диапазоне от 0 to 2^16 -1, тогда возможны <<24 > комбинации возможных входов, поэтому очевидным Pigeonhole Принцип, нам нужен выходной размер не менее 2^16 * (2^16 -1), который равен 2^32 - 2^16, или, другими словами, карта чисел бит 32 должна быть практически идеальной. Это может быть мало практическим в мире программирования.

Функция спаривания Кантора:

(a + b) * (a + b + 1) / 2 + a; where a, b >= 0

Отображение для двух максимальных 16-битных целых чисел (65535, 65535) будет 8589803520, которое, как вы видите, не может быть помещено в 32 бита.

Введите функцию Szudzik:

a >= b ? a * a + a + b : a + b * b;  where a, b >= 0

Отображение для (65535, 65535) теперь будет 4294967295, которое, как вы видите, представляет собой 32-битное (от 0 до 2 ^ 32 -1) целое число. Именно здесь это решение идеально, оно просто использует каждую точку в этом пространстве, поэтому ничто не может получить больше пространства.


Теперь, учитывая тот факт, что мы обычно имеем дело с подписанными реализациями чисел различных размеров в языках/фреймах, рассмотрим signed 16 битовые целые числа от -(2^15) to 2^15 -1 (в дальнейшем мы увидим, как расширить даже вывод до диапазон по значению диапазона). Поскольку a и b должны быть положительными, они варьируются от 0 to 2^15 - 1.

Функция спаривания Кантора:

Отображение для двух максимальных 16-разрядных целых чисел со знаком (32767, 32767) будет 2147418112, что просто не соответствует максимальному значению для 32-битного целого числа.

Теперь функция Szudzik:

(32767, 32767) = > 1073741823, намного меньше..

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

Функция спаривания Кантора:

A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
(A + B) * (A + B + 1) / 2 + A;

(- 32768, -32768) = > 8589803520, который является Int64. 64-разрядный выход для 16-битных входов может быть настолько непростительным!

Функция Szudzik:

A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
A >= B ? A * A + A + B : A + B * B;

(- 32768, -32768) = > 4294967295, который 32 бит для неподписанного диапазона или 64 бит для подписанного диапазона, но все же лучше.

Теперь все это, в то время как вывод всегда был положительным. В подписанном мире это будет еще больше экономии пространства, если мы можем перенести половину вывода на отрицательную ось. Вы можете сделать это так для Szudzik's:

A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
C = (A >= B ? A * A + A + B : A + B * B) / 2;
a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;

(-32768, 32767) => -2147483648

(32767, -32768) => -2147450880

(0, 0) => 0 

(32767, 32767) => 2147418112

(-32768, -32768) => 2147483647

Что я делаю: после применения веса 2 к входам и прохождения через функцию, я затем делят вывод на два и переношу некоторые из них на отрицательную ось, умножая на -1.

См. результаты, для любого ввода в диапазоне подписанного числа бит 16, выход находится в пределах подписанного целого числа бит 32, которое является прохладным. Я не уверен, как сделать то же самое для функции спаривания Кантора, но не пытался, насколько это не так эффективно. Кроме того, больше вычислений, связанных с функцией спаривания Кантора, означает, что он медленнее тоже.

Вот реализация С#.

public static long PerfectlyHashThem(int a, int b)
{
    var A = (ulong)(a >= 0 ? 2 * (long)a : -2 * (long)a - 1);
    var B = (ulong)(b >= 0 ? 2 * (long)b : -2 * (long)b - 1);
    var C = (long)((A >= B ? A * A + A + B : A + B * B) / 2);
    return a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
}

public static int PerfectlyHashThem(short a, short b)
{
    var A = (uint)(a >= 0 ? 2 * a : -2 * a - 1);
    var B = (uint)(b >= 0 ? 2 * b : -2 * b - 1);
    var C = (int)((A >= B ? A * A + A + B : A + B * B) / 2);
    return a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
}

Поскольку промежуточные вычисления могут превышать пределы 2N целого числа со знаком, я использовал целочисленный тип 4N (последнее деление на 2 возвращает результат к 2N).

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

Ответ 3

Если A и B могут быть выражены с помощью 2 байтов, вы можете объединить их на 4 байта. Положите A на наиболее значительную половину и B на наименее значимую половину.

В языке C это дает (при условии sizeof (short) = 2 и sizeof (int) = 4):

int combine(short A, short B)
{
    return A<<16 | B;
}

short getA(int C)
{
    return C>>16;
}

short getB(int C)
{
    return C & 0xFFFF;
}

Ответ 4

Возможно ли это? Вы объединяете два целых числа. Они оба имеют диапазон -2,147,483,648 до 2,147,483,647, но вы будете принимать только положительные результаты. Это составляет 2147483647 ^ 2 = 4 61169E + 18 комбинаций. Так как каждая комбинация должна быть уникальной И привести к целому числу, вам понадобится какое-то волшебное целое число, которое может содержать это количество чисел.

Или моя логика испорчена?

Ответ 5

Пусть число a будет первым, b вторым. Пусть p - a+1 -е простое число, q - b+1 -th простое число

Тогда результат pq, если a<b, или 2pq, если a>b. Если a=b, пусть это будет p^2.

Ответ 6

Стандартным математическим способом для целых положительных чисел является использование единственности простой факторизации.

f( x, y ) -> 2^x * 3^y

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

Вы можете изменить это, чтобы иметь дело с отрицательными x и y, кодируя флаги с полномочиями 5 и 7 членов.

например.

f( x, y ) -> 2^|x| * 3^|y| * 5^(x<0) * 7^(y<0)

Ответ 7

f(a, b) = s(a+b) + a, где s(n) = n*(n+1)/2

  • Это функция - она ​​детерминирована.
  • Он также инъективен - f отображает разные значения для разных (a, b) пар. Вы можете доказать это с использованием факта: s(a+b+1)-s(a+b) = a+b+1 < a.
  • Он возвращает довольно небольшие значения - хорошо, если вы собираетесь использовать его для индексирования массива, так как массив не должен быть большим.
  • Это кэширование - если две (a, b) пары близки друг к другу, то f сопоставляет числа, близкие друг к другу (по сравнению с другими методами).

Я не понял, о чем вы говорите:

всегда должно давать целое число на либо положительный, либо отрицательный сторона целых чисел

Как я могу писать (больше, чем), (меньше) символов в этом форуме?

Ответ 8

Не сложно построить отображение:

   1  2  3  4  5  use this mapping if (a,b) != (b,a)
1  0  1  3  6 10
2  2  4  7 11 16
3  5  8 12 17 23
4  9 13 18 24 31
5 14 19 25 32 40

   1  2  3  4  5 use this mapping if (a,b) == (b,a) (mirror)
1  0  1  2  4  6
2  1  3  5  7 10
3  2  5  8 11 14
4  4  8 11 15 19
5  6 10 14 19 24


    0  1 -1  2 -2 use this if you need negative/positive
 0  0  1  2  4  6
 1  1  3  5  7 10
-1  2  5  8 11 14
 2  4  8 11 15 19
-2  6 10 14 19 24

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

Ответ 9

Хотя ответ Stephan202 является единственным действительно общим, для целых чисел в ограниченном диапазоне вы можете сделать лучше. Например, если ваш диапазон равен 0,10,000, вы можете сделать:

#define RANGE_MIN 0
#define RANGE_MAX 10000

unsigned int merge(unsigned int x, unsigned int y)
{
    return (x * (RANGE_MAX - RANGE_MIN + 1)) + y;
}

void split(unsigned int v, unsigned int &x, unsigned int &y)
{
    x = RANGE_MIN + (v / (RANGE_MAX - RANGE_MIN + 1));
    y = RANGE_MIN + (v % (RANGE_MAX - RANGE_MIN + 1));
}

Результаты могут вписываться в одно целое число в диапазоне до квадратного корня из целочисленного типа. Это намного эффективнее, чем Stephan202 более общий метод. Также значительно проще декодировать; не требующих квадратных корней, для начала:)

Ответ 10

Для положительных целых чисел в качестве аргументов и где порядок аргументов не имеет значения:

Ответ 11

Отметьте это: http://en.wikipedia.org/wiki/Pigeonhole_principle. Если A, B и C имеют один и тот же тип, это невозможно. Если A и B являются 16-битными целыми числами, а C - 32-битными, вы можете просто использовать перенос.

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

Ответ 12

Вот расширение кода @DoctorJ для неограниченных целых чисел на основе метода, заданного @nawfal. Он может кодировать и декодировать. Он работает с обычными массивами и массивами numpy.

#!/usr/bin/env python
from numbers import Integral    

def tuple_to_int(tup):
    """:Return: the unique non-negative integer encoding of a tuple of non-negative integers."""
    if len(tup) == 0:  # normally do if not tup, but doesn't work with np
        raise ValueError('Cannot encode empty tuple')
    if len(tup) == 1:
        x = tup[0]
        if not isinstance(x, Integral):
            raise ValueError('Can only encode integers')
        return x
    elif len(tup) == 2:
        # print("len=2")
        x, y = tuple_to_int(tup[0:1]), tuple_to_int(tup[1:2])  # Just to validate x and y

        X = 2 * x if x >= 0 else -2 * x - 1  # map x to positive integers
        Y = 2 * y if y >= 0 else -2 * y - 1  # map y to positive integers
        Z = (X * X + X + Y) if X >= Y else (X + Y * Y)  # encode

        # Map evens onto positives
        if (x >= 0 and y >= 0):
            return Z // 2
        elif (x < 0 and y >= 0 and X >= Y):
            return Z // 2
        elif (x < 0 and y < 0 and X < Y):
            return Z // 2
        # Map odds onto negative
        else:
            return (-Z - 1) // 2
    else:
        return tuple_to_int((tuple_to_int(tup[:2]),) + tuple(tup[2:]))  # ***speed up tuple(tup[2:])?***


def int_to_tuple(num, size=2):
    """:Return: the unique tuple of length `size` that encodes to `num`."""
    if not isinstance(num, Integral):
        raise ValueError('Can only encode integers (got {})'.format(num))
    if not isinstance(size, Integral) or size < 1:
        raise ValueError('Tuple is the wrong size ({})'.format(size))
    if size == 1:
        return (num,)
    elif size == 2:

        # Mapping onto positive integers
        Z = -2 * num - 1 if num < 0 else 2 * num

        # Reversing Pairing
        s = isqrt(Z)
        if Z - s * s < s:
            X, Y = Z - s * s, s
        else:
            X, Y = s, Z - s * s - s

        # Undoing mappint to positive integers
        x = (X + 1) // -2 if X % 2 else X // 2  # True if X not divisible by 2
        y = (Y + 1) // -2 if Y % 2 else Y // 2  # True if Y not divisible by 2

        return x, y

    else:
        x, y = int_to_tuple(num, 2)
        return int_to_tuple(x, size - 1) + (y,)


def isqrt(n):
    """":Return: the largest integer x for which x * x does not exceed n."""
    # Newton method, via http://stackoverflow.com/a/15391420
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

Ответ 13

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

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

Предполагая 32-разрядное целое число, у вас есть 2147483647 положительных целых чисел. Выбор двух из них, где порядок не имеет значения и с повторением дает 2305843008139952128 комбинаций. Это не подходит для набора из 32-битных целых чисел.

Вы можете, однако, поместить это сопоставление в 61 бит. Использование 64-битного целого числа, вероятно, проще всего. Установите высокое слово на меньшее целое число, а нижнее - на большее.

Ответ 14

Как насчет чего-то гораздо более простого: Учитывая два числа, A и B позволяют str быть конкатенацией: 'A' + ';' + "Б". Тогда пусть выводом будет хеш (str). Я знаю, что это не математический ответ, а простой скрипт на Python (который имеет встроенную хэш-функцию) должен делать эту работу.

Ответ 15

имеем два числа B и C, кодируя их в одно число A

A = B + C * N

где

B = A% N = B

C = A/N = C

Ответ 16

Учитывая положительные целые числа A и B, пусть D = количество цифр A имеет, а E = количество цифр B имеет. Результатом может быть объединение D, 0, E, 0, A и B.

Пример: A = 300, B = 12. D = 3, E = 2, результат = 302030012. Это использует тот факт, что единственным числом, начинающимся с 0, является 0,

Pro: Легко кодировать, легко декодировать, удобочитаемый, сначала можно сравнить значимые цифры, возможность сравнения без вычислений, простая проверка ошибок.

Минусы: размер результатов является проблемой. Но это нормально, почему мы в любом случае храним неограниченные целые числа в компьютере.

Ответ 17

Скажем, у вас есть 32-разрядное целое число, почему бы просто не переместить A в первую 16-разрядную половину, а B в другую?

def vec_pack(vec):
    return vec[0] + vec[1] * 65536;


def vec_unpack(number):
    return [number % 65536, int(number / 65536)];

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

a = vec_pack([2,4])
b = vec_pack([1,2])

print(vec_unpack(a+b)) # [3, 6] Vector addition
print(vec_unpack(a-b)) # [1, 2] Vector subtraction
print(vec_unpack(a*2)) # [4, 8] Vector multiplication

который проходит через эти две точки