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

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

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

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


person harm    schedule 28.05.2009    source источник
comment
Вам следует уточнить, имеете ли вы в виду целые числа в программном обеспечении или целые числа в математике. В программном обеспечении вы выбираете любой целочисленный тип, и он будет иметь размер, поэтому у вас их конечное число, поэтому решения нет (если, конечно, ваши входные данные не будут гарантированно находиться в каком-то диапазоне, а ваш результат может быть любое целое число). По математике см. Решение ASk.   -  person Daniel Daranas    schedule 28.05.2009
comment
Я говорю об ограниченных целых числах в низком положительном диапазоне. Скажите от 0 до 10 000   -  person harm    schedule 01.06.2009
comment
@harm: А как насчет 10,001*A + B?   -  person BlueRaja - Danny Pflughoeft    schedule 13.07.2011
comment
Я нашел следующие функции PHP: gist.github.com/hannesl/8031402   -  person cakan    schedule 16.06.2016
comment
Если порядок не имеет значения, например: (3,12) и (12,3) дают тот же результат, я использую A + B + A * B   -  person Sodj    schedule 08.05.2018


Ответы (19)


Вы ищете биективное NxN -> N отображение. Они используются, например, для ласточкин хвост. Взгляните на этот PDF-файл, чтобы познакомиться с так называемыми < em> функции сопряжения. В Википедии представлена ​​особая функция сопряжения, а именно функция сопряжения Кантора:

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

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

  • Как пояснили другие, если вы планируете реализовать функцию сопряжения, вы вскоре можете обнаружить, что вам нужны произвольно большие целые числа (bignums).
  • Если вы не хотите проводить различие между парами (a, b) и (b, a), отсортируйте a и b перед применением функции сопряжения.
  • Actually I lied. You are looking for a bijective ZxZ -> N mapping. Cantor's function only works on non-negative numbers. This is not a problem however, because it's easy to define a bijection f : Z -> N, like so:
    • f(n) = n * 2 if n >= 0
    • f(n) = -n * 2 - 1 if n < 0
person Stephan202    schedule 28.05.2009
comment
+1 Думаю, это правильный ответ для неограниченных целых чисел. - person Unknown; 28.05.2009
comment
Как я могу снова получить значение k1, k2? - person MinuMaster; 22.04.2012
comment
@MinuMaster: это описано в той же статье Википедии в разделе Инвертирование функции сопряжения Кантора < / i>. - person Stephan202; 23.04.2012
comment
См. Также функцию Szudzik, объясненную newfal ниже. - person OliJG; 18.12.2012
comment
Хотя это верно для неограниченных целых чисел, это не лучше для ограниченных целых чисел. Я думаю, что комментарий @ blue-raja имеет наибольший смысл. - person Kardasis; 16.07.2013
comment
Это не работает для неположительных целых чисел. Пример: (0, 0) - ›0, (-1, 0) -› 0. - person tkrishtop; 30.03.2019

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

Введите функцию Судзика:

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-битного целого числа со знаком.

Теперь функция Судзика:

(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-битных входов может быть таким непростительным !!

Функция Судзика:

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-битным для подписанного диапазона, но все же лучше.

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

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-битового целого числа со знаком, что неплохо. Я не уверен, как сделать то же самое для функции сопряжения Кантора, но не пробовал так много, как это не так эффективно. Кроме того, чем больше вычислений выполняется функция связывания Кантора, тем медленнее она тоже.

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

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

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

person nawfal    schedule 14.12.2012
comment
Какой была бы модифицированная функция unhash для целых чисел со знаком? - person Arets Paeglis; 03.12.2013
comment
Этот ответ меня смущает. Если вы хотите отобразить (0,0) через (65535,65535) в одно число, тогда a<<16 + b лучше во всех отношениях (быстрее, проще, проще для понимания, более очевидно). Если вместо этого вы хотите, чтобы (-32768,-32768) в (327687,327687), просто сначала задайте тему 32768. - person BlueRaja - Danny Pflughoeft; 15.05.2014
comment
@ BlueRaja-DannyPflughoeft, ты прав. Мой ответ будет действительным, если диапазон не ограничен или неизвестен. Буду обновлять. Я написал это до того, как предел имел для меня значение. Я давно задумал редактировать этот ответ. Я скоро найду время. - person nawfal; 15.05.2014
comment
Работает ли функция Судзика для комбинаций или перестановок. Вроде перестановки, да? Если я хочу использовать для комбинирования, могу ли я просто исключить части алгоритма IF и Else? - person Jamie Marshall; 31.08.2016
comment
Вот реализация Python функции Судзика, обобщенная для кортежей любой длины: gitlab.com/snippets/32559 - person Doctor J; 01.12.2016
comment
Ваша хеш-функция PerfectlyHashThem (65535, 65535) = 8,589,803,520 = 33 бита = более 4 байтов. Это не 4294967295, как вы упомянули. Кстати, я согласен с @ BlueRaja-DannyPflughoeft, если мы собираемся объединить 2 * 32-битное число в один 64-битный, то битовое перемещение лучше - person Hung Doan; 11.01.2019

Если A и B можно выразить двумя байтами, вы можете объединить их в 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;
}
person mouviciel    schedule 28.05.2009
comment
combine() должен return (unsigned short)(A<<16) | (unsigned short)(B); Так, чтобы отрицательные числа можно было правильно упаковать. - person Andy; 02.01.2013
comment
@Andy A<<16 выйдет за пределы поля. Должно быть return (unsigned int)(A<<16) | (unsigned short)(B); - person DanSkeel; 31.03.2018

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

Или моя логика ошибочна?

person Boris Callens    schedule 28.05.2009
comment
+1 Я тоже так думаю (хотя я сделал расчет, сказав, что порядок A и B не имеет значения) - person lc.; 28.05.2009
comment
Да, ваша логика верна по принципу ячейки. К сожалению, спрашивающий не уточнил, ограничено целое число или нет. - person Unknown; 28.05.2009
comment
Да, я подумал об этом позже, но я подумал, что сообщение по сути то же самое, поэтому я не стал пересчитывать. - person Boris Callens; 28.05.2009
comment
Также я только что понял, что мне нужно снова взять свои учебники по расчету шансов (дословный перевод с голландского). - person Boris Callens; 28.05.2009
comment
@Boris: Кансрекенинг - это теория вероятностей. - person Stephan202; 28.05.2009

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

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

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

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

e.g.

f( x, y ) -> 2^|x| * 3^|y| * 5^(x<0) * 7^(y<0)
person CB Bailey    schedule 28.05.2009
comment
Математика в порядке. Но, как говорит Борис, если вы хотите запустить это как компьютерную программу, вы должны принять во внимание конечность машины. Алгоритм будет правильно работать для подмножества целых чисел, представленных на соответствующей машине. - person Yuval F; 28.05.2009
comment
Я сказал об этом во втором абзаце. Теги в вопросе указывают на «алгоритм», «математический» и «детерминированный», а не на какой-либо конкретный язык. Диапазон ввода не может быть ограничен, и среда может иметь неограниченный целочисленный тип bigint. - person CB Bailey; 28.05.2009

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

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

person ASk    schedule 28.05.2009
comment
Я сомневаюсь, что вам нужно решение NP. - person user44242; 28.05.2009
comment
Разве это не дает тот же результат для a = 5, b = 14 и a = 6, b = 15? - person Lieven Keersmaekers; 28.05.2009
comment
Два произведения двух разных простых чисел не могут иметь одинаковый результат (уникальное разложение на простые множители) a = 5, b = 14 - ›результат 13 * 47 = 611 a = 6, b = 15 -› результат 17 * 53 = 901 - person ASk; 28.05.2009

Построить отображение не так уж и сложно:

   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, немного сложнее.

person Dolphin    schedule 29.05.2009

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 сопоставляет им числа, которые близки друг к другу (по сравнению с другими методами).

Я не понял, что Вы имеете в виду под:

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

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

person libeako    schedule 28.05.2009
comment
Больше и меньше символов должно работать в пределах backtick escapes. - person TRiG; 01.12.2010
comment
Это эквивалентно функции сопряжения Кантора и поэтому не работает с отрицательными целыми числами. - person Davor Josipovic; 29.05.2020

Хотя ответ 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. Кроме того, его значительно проще декодировать; для начала, не требуя квадратных корней :)

person bdonlan    schedule 01.02.2011
comment
Возможно ли это для поплавков? - person Lukas; 08.03.2018

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

  1. Вот функция неупорядоченного сопряжения:

    <x, y> = x * y + trunc((|x - y| - 1)^2 / 4) = <y, x>
    
  2. Для x ≠ y есть уникальная функция неупорядоченного сопряжения < / а>:

    <x, y> = if x < y:
               x * (y - 1) + trunc((y - x - 2)^2 / 4)
             if x > y:
               (x - 1) * y + trunc((x - y - 2)^2 / 4)
           = <y, x>
    
person ma11hew28    schedule 10.03.2014

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

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

person Groo    schedule 28.05.2009

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

#!/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's 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
person NStarman    schedule 19.02.2017

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

person Madhav Nakar    schedule 11.04.2018
comment
но (8,11) и (81,1) отображаются на один и тот же номер 811 - person Leevi L; 25.04.2019
comment
Это хороший момент. Вы можете решить эту проблему, просто добавив символ посередине. Итак, для (8, 11) хеш-строка 8-11, а для (81, 1) - строка 81-1. Итак, в общем случае для (A, B) строка A-B хешируется. (Я знаю, это звучит хакерски, но должно работать). - person Madhav Nakar; 25.04.2019
comment
это также неправильно, потому что эта задача состоит в том, чтобы сопоставить два целых числа с новым целым числом, а не строку с символом - person Leevi L; 26.04.2019
comment
Я исхожу с точки зрения CS, а не с математической (математические решения смотрите на ответы выше). Я беру два целых числа, превращаю их в строку, а затем превращаю в целое число. По сути, да, я сопоставляю два целых числа с новым. - person Madhav Nakar; 28.04.2019

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

A = B + C * N

где

B=A % N = B

C=A / N = C

person Ankur Chauhan    schedule 15.11.2015
comment
Как выбрать N, чтобы сделать это представление уникальным? Если вы решите эту проблему, чем этот ответ будет отличаться от приведенных выше? - person Prune; 15.11.2015
comment
Вы должны добавить, что N должно быть больше, чем B и C. - person Radoslav Stoyanov; 26.03.2019

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

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

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

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

person lc.    schedule 28.05.2009

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

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


def vec_unpack(number):
    return [number % 65536, 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] Scalar multiplication
person Basic Block    schedule 23.08.2019

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

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

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

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

person Ban Piao    schedule 11.01.2019

Если вам нужно больше контроля, например выделить X бит для первого числа и Y бит для второго числа, вы можете использовать этот код:

class NumsCombiner
{

    int num_a_bits_size;
    int num_b_bits_size;

    int BitsExtract(int number, int k, int p)
    {
        return (((1 << k) - 1) & (number >> (p - 1)));
    }

public:
    NumsCombiner(int num_a_bits_size, int num_b_bits_size)
    {
        this->num_a_bits_size = num_a_bits_size;
        this->num_b_bits_size = num_b_bits_size;
    }

    int StoreAB(int num_a, int num_b)
    {
        return (num_b << num_a_bits_size) | num_a;
    }

    int GetNumA(int bnum)
    {
        return BitsExtract(bnum, num_a_bits_size, 1);
    }

    int GetNumB(int bnum)
    {
        return BitsExtract(bnum, num_b_bits_size, num_a_bits_size + 1);
    }
};

Всего я использую 32 бита. Идея здесь в том, что если вы хотите, например, чтобы первое число было до 10 бит, а второе число было до 12 бит, вы можете сделать это:

NumsCombiner nums_mapper(10/*bits for first number*/, 12/*bits for second number*/);

Теперь вы можете сохранить в num_a максимальное число, равное 2^10 - 1 = 1023, и в num_b максимальное значение 2^12 - 1 = 4095.

Чтобы установить значение для num A и num B:

int bnum = nums_mapper.StoreAB(10/*value for a*/, 12 /*value from b*/);

Теперь bnum - это все биты (всего 32 бита. Вы можете изменить код, чтобы использовать 64 бита). Чтобы получить num a:

int a = nums_mapper.GetNumA(bnum);

Чтобы получить число b:

int b = nums_mapper.GetNumB(bnum);

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

Спасибо за источник: https://www.geeksforgeeks.org/extract-k-bits-given-position-number/ для функции извлечения битов, а также спасибо mouviciel за ответ в этом посте. Используя их в источниках, я смог найти более продвинутое решение.

person gil123    schedule 02.03.2020

Мы можем закодировать два числа в одно в пространстве O (1) и времени O (N). Предположим, вы хотите кодировать числа в диапазоне 0-9 в один, например. 5 и 6. Как это сделать? Простой,

  5*10 + 6 = 56. 
   
    5 can be obtained by doing 56/10 
    6 can be obtained by doing 56%10.

Даже для двузначного целого числа, скажем, 56 и 45, 56 * 100 + 45 = 5645. Мы снова можем получить отдельные числа, выполнив 5645/100 и 5645% 100

Но для массива размера n, например. a = {4,0,2,1,3}, допустим, мы хотим закодировать 3 и 4, поэтому:

 3 * 5 + 4 = 19               OR         3 + 5 * 4 = 23
 3 :- 19 / 5 = 3                         3 :- 23 % 5 = 3
 4 :- 19 % 5 = 4                         4 :- 23 / 5 = 4

Обобщая его, получаем

    x * n + y     OR       x + n * y

Но мы также должны позаботиться об изменении значения; так что это заканчивается как

    (x%n)*n + y  OR x + n*(y%n)

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

person L.Lovegood    schedule 15.11.2020