2つの正の整数AとBを想像してください。これら2つを単一の整数Cに結合したいです。
Cに結合する他の整数DとEはあり得ません。したがって、それらを加算演算子と結合しても機能しません。例:30 + 10 = 40 = 40 + 0 = 39 + 1連結も機能しません。例:「31」+「2」= 312 =「3」+「12」
この組み合わせ演算も決定論的である必要があります(常に同じ入力で同じ結果が得られます)andは常に整数の正または負のいずれかの整数を生成する必要があります。
全単射NxN -> N
マッピングを探しています。これらは、例えば dovetailing いわゆるペアリング関数の概要については このPDF をご覧ください。ウィキペディアには、特定のペアリング機能、つまり Cantorペアリング機能 が導入されています。
3つの発言:
ZxZ -> N
マッピングを探しています。 Cantorの関数は、負でない数でのみ機能します。しかし、これは問題ではありません。なぜなら、次のように全単射f : Z -> N
を定義するのは簡単だからです。キャンターペアリング関数 は、そのシンプルで高速かつスペース効率の高いことを考えると、実際には優れたものの1つですが、Wolframでさらに優れたものが公開されています マシューシュズジク著、こちら Cantorペアリング機能の(比較的)制限は、入力が2つのN
ビット整数である場合、エンコードされた結果の範囲が常に2N
ビット整数の制限内に収まらないことです。つまり、私の入力が16
の範囲の2つの0 to 2^16 -1
ビット整数である場合、入力の2^16 * (2^16 -1)
の組み合わせが可能なため、明らかな Pigeonhole Principle により、少なくともサイズの出力が必要です。 2^16 * (2^16 -1)
は、2^32 - 2^16
と同じです。つまり、32
ビット番号のマップは理想的には実行可能です。これは、プログラミングの世界ではほとんど実用的ではないかもしれません。
キャンターペアリング機能:
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
2つの最大16ビット整数(65535、65535)のマッピングは8589803520になります。これは、ご覧のように32ビットに収めることができません。
EnterSzudzikの関数:
a >= b ? a * a + a + b : a + b * b; where a, b >= 0
(65535、65535)のマッピングは4294967295になります。これは、ご覧のとおり、32ビット(0〜2 ^ 32 -1)整数です。これは、このソリューションが理想的な場所であり、単にそのスペース内のすべての単一ポイントを利用するため、より効率的なスペースは得られません。
言語/フレームワークでさまざまなサイズの番号の署名付き実装を通常処理するという事実を考慮して、-(2^15) to 2^15 -1
(後で出力を拡張して符号付きにまたがる方法についてはsigned 16
ビット整数を検討してみましょう範囲)。 a
およびb
は正でなければならないため、0 to 2^15 - 1
の範囲です。
キャンターペアリング機能:
2つの最大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)。 16ビット入力の64ビット出力は、非常に許容できない場合があります。
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の場合は次のようにできます。
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
の重みを入力に適用し、関数を通過した後、出力を2で除算し、それらの一部を-1
で乗算して負の軸にします。
結果を参照してください。符号付き16
ビット数の範囲の入力については、出力は符号付き32
ビット整数の制限内に収まります。 Cantorペアリング機能について同じ方法を実行する方法はわかりませんが、効率的でないほど多くのことを試みませんでした。 さらに、Cantorペアリング機能に関係する計算が増えると、その速度も遅くなります。
これが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
に戻されます)。
私が代替ソリューションで提供したリンクは、空間内のすべての単一ポイントを利用する関数のグラフをうまく描いています。 一対の座標を1つの数値に可逆的に一意にエンコードできることは驚くべきことです!数字の魔法の世界!!
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;
}
これも可能ですか?
2つの整数を組み合わせています。両方の範囲は-2,147,483,648〜2,147,483,647ですが、正の値のみを使用します。これにより、2147483647 ^ 2 = 4,61169E + 18の組み合わせになります。各組み合わせは一意である必要があり、結果として整数になるため、この数の数値を含むことができる何らかの魔法の整数が必要になります。
または、私のロジックに欠陥がありますか?
番号a
を最初、b
を2番目にします。 p
をa+1
-番目の素数、q
をb+1
-番目の素数とする
次に、結果は、a<b,
の場合はpq
、または2pq
の場合はa>b
です。 a=b
の場合、p^2
とします。
正の整数の標準的な数学的方法は、素因数分解の一意性を使用することです。
f( x, y ) -> 2^x * 3^y
欠点は、画像が非常に広い範囲の整数にまたがる傾向があるため、コンピューターアルゴリズムでマッピングを表現する場合、結果に適したタイプを選択する際に問題が発生する可能性があることです。
これを変更して、5項と7項の累乗でフラグをエンコードすることにより、負のx
およびy
を処理できます。
例えば.
f( x, y ) -> 2^|x| * 3^|y| * 5^(x<0) * 7^(y<0)
引数として正の整数を使用し、引数の順序が重要でない場合:
<x, y> = x * y + trunc((|x - y| - 1)^2 / 4) = <y, x>
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>
f(a, b) = s(a+b) + a
、ここでs(n) = n*(n+1)/2
s(a+b+1)-s(a+b) = a+b+1 < a
という事実を使用します。あなたが何を意味するのか理解できませんでした:
整数の正または負のいずれかの側で常に整数を生成する必要があります
このフォーラムで(より大きい)、(より小さい)文字を書くにはどうすればよいですか?
マッピングを構築するのはそれほど難しくありません:
1 2 3 4 5(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 (a、b)==(b、a)(鏡) 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ネガティブ/ポジティブが必要な場合に使用 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の値を取得する方法を理解することは、もう少し困難です。
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のより一般的な方法よりもわずかに効率的にパックされます。デコードもかなり簡単です。最初に平方根を必要としません:)
@nawfalで指定されたメソッドに基づいて、@ DoctorJのコードを無制限の整数に拡張します。エンコードおよびデコードできます。通常の配列と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'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
これを確認してください: http://en.wikipedia.org/wiki/Pigeonhole_principle 。 A、B、Cが同じタイプの場合、実行できません。 AとBが16ビット整数で、Cが32ビットの場合、単純にシフトを使用できます。
ハッシュアルゴリズムの本質は、異なる入力ごとに一意のハッシュを提供できないことです。
もっと簡単な方法はどうですか:AとBの2つの数値を与えて、strを連結させます: 'A' + ';' + 'B'。次に、出力をhash(str)にします。これは数学的な答えではないことを知っていますが、簡単なpython(ハッシュ関数が組み込まれています)スクリプトが仕事をするはずです。
あなたが提案することは不可能です。常に衝突が発生します。
2つのオブジェクトを別の単一のセットにマッピングするには、マッピングされたセットの予想される組み合わせの数の最小サイズが必要です。
32ビット整数を想定すると、2147483647個の正の整数があります。順序が重要ではない場所で繰り返しを使用してこれらの2つを選択すると、2305843008139952128の組み合わせが生成されます。これは、32ビット整数のセットにはうまく適合しません。
ただし、このマッピングを61ビットに収めることができます。 64ビット整数を使用するのがおそらく最も簡単です。上位のWordを小さい整数に設定し、下位のWordを大きい整数に設定します。
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
それはこれらの2つのポイントを通過します
2つの数値BとCを持ち、それらを単一の数値Aにエンコードします
A = B + C * N
どこ
B = A%N = B
C = A/N = C
正の整数AおよびBが与えられた場合、D = Aの桁数、E = Bの桁数とします。結果は、D、0、E、0、A、およびBの連結になります。
例:A = 300、B =12。D= 3、E = 2結果=302030012。これは、0で始まる唯一の数が0であるという事実を利用しています。
プロ:エンコードが簡単、デコードが簡単、人間が読める、有効数字を最初に比較できる、計算なしで比較できる、簡単なエラーチェック.
短所:結果のサイズが問題です。しかし、それは大丈夫です、とにかくコンピューターに無制限の整数を保存するのはなぜですか。