Ánh xạ hai số nguyên thành một, theo cách duy nhất và xác định


234

Tưởng tượng hai số nguyên dương A và B. Tôi muốn kết hợp hai số nguyên này thành một số nguyên C.

Không thể có số nguyên D và E nào khác kết hợp với C. Vì vậy, việc kết hợp chúng với toán tử cộng không hoạt động. Ví dụ: 30 + 10 = 40 = 40 + 0 = 39 + 1 Không kết hợp hoạt động. Ví dụ: "31" + "2" = 312 = "3" + "12"

Hoạt động kết hợp này cũng phải có tính xác định (luôn mang lại kết quả tương tự với cùng một đầu vào) phải luôn mang lại một số nguyên ở phía dương hoặc âm của số nguyên.


10
Bạn nên làm rõ nếu bạn có nghĩa là số nguyên trong phần mềm hoặc số nguyên trong toán học. Trong phần mềm, bạn chọn bất kỳ loại số nguyên nào và nó sẽ có kích thước, do đó bạn có số lượng hữu hạn của chúng, vì vậy không có giải pháp nào (tất nhiên trừ khi dữ liệu đầu vào của bạn được đảm bảo trong phạm vi và đầu ra của bạn có thể bất kỳ số nguyên). Trong toán học xem giải pháp của ASk.
Daniel Daranas

Tôi đang nói về số nguyên bị ràng buộc trong một phạm vi thấp, tích cực. Nói 0 đến 10.000
hại

27
@harm: Vậy còn về 10,001*A + B?
BlueRaja - Daniel Pflughoeft

2
Tôi đã tìm thấy các hàm PHP này: gist.github.com/hannesl/8031402
cakan

Nếu thứ tự không quan trọng, ví dụ: (3,12) & (12,3) cho kết quả tương tự, tôi sử dụng "A + B" + "A * B"
Sodj

Câu trả lời:


233

Bạn đang tìm kiếm một NxN -> Nbản đồ phỏng đoán . Chúng được sử dụng cho ví dụ như dovetail . Hãy xem bản PDF này để biết giới thiệu về cái gọi là chức năng ghép nối . Wikipedia giới thiệu một chức năng ghép nối cụ thể, cụ thể là chức năng ghép nối Cantor :

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

Ba nhận xét:

  • Như những người khác đã làm rõ, nếu bạn có kế hoạch thực hiện chức năng ghép nối, bạn có thể sớm thấy bạn cần các số nguyên lớn (bignums) tùy ý.
  • Nếu bạn không muốn phân biệt giữa các cặp (a, b) và (b, a), thì hãy sắp xếp a và b trước khi áp dụng chức năng ghép nối.
  • Thật ra tôi đã nói dối. Bạn đang tìm kiếm một ZxZ -> Nbản đồ phỏng đoán . Chức năng của Cantor chỉ hoạt động trên các số không âm. Tuy nhiên, đây không phải là vấn đề vì dễ xác định một mệnh đề f : Z -> N, như vậy:
    • f (n) = n * 2 nếu n> = 0
    • f (n) = -n * 2 - 1 nếu n <0

13
+1 Tôi nghĩ rằng đây là câu trả lời chính xác cho các số nguyên không giới hạn.
Không biết

4
Làm thế nào tôi có thể nhận lại giá trị của k1, k2?
MinuMaster

3
@MinuMaster: được mô tả trong cùng một bài viết Wikipedia, trong phần Đảo ngược chức năng ghép nối Cantor .
Stephan202

4
Xem thêm chức năng của Szudzik, được giải thích bởi newfal dưới đây.
OliJG

1
Mặc dù điều này đúng với các số nguyên không giới hạn, nhưng nó không tốt nhất cho các số nguyên bị ràng buộc. Tôi nghĩ rằng bình luận của @ blue-raja có ý nghĩa nhất cho đến nay.
Kardocation

225

Chức năng ghép nối Cantor thực sự là một trong những chức năng tốt hơn ngoài kia vì nó đơn giản, nhanh và hiệu quả về không gian, nhưng có một thứ thậm chí còn được xuất bản tốt hơn tại Wolfram của Matthew Szudzik, ở đây . Hạn chế của chức năng ghép cặp Cantor (tương đối) là phạm vi kết quả được mã hóa không phải luôn nằm trong giới hạn của một 2Nsố nguyên bit nếu đầu vào là hai Nsố nguyên bit. Đó là, nếu đầu vào của tôi là hai 16số nguyên bit khác nhau 0 to 2^16 -1, thì có thể 2^16 * (2^16 -1)kết hợp các đầu vào, do đó, theo Nguyên tắc Pigeonhole rõ ràng , chúng ta cần một đầu ra có kích thước tối thiểu 2^16 * (2^16 -1), bằng 2^32 - 2^16, hay nói cách khác là bản đồ32số bit nên khả thi lý tưởng. Điều này có thể không có ít tầm quan trọng thực tế trong thế giới lập trình.

Chức năng ghép cặp :

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

Ánh xạ cho hai số nguyên tối đa 16 bit (65535, 65535) sẽ là 8589803520 mà bạn thấy không thể phù hợp với 32 bit.

Nhập chức năng của Szudzik :

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

Ánh xạ cho (65535, 65535) bây giờ sẽ là 4294967295 mà bạn thấy là số nguyên 32 bit (0 đến 2 ^ 32 -1). Đây là nơi giải pháp này là lý tưởng, nó chỉ đơn giản là sử dụng mọi điểm duy nhất trong không gian đó, vì vậy không có gì có thể có được không gian hiệu quả hơn.


Bây giờ xem xét thực tế là chúng ta thường xử lý các triển khai đã ký của các số có kích cỡ khác nhau trong các ngôn ngữ / khung, hãy xem xét signed 16các số nguyên bit khác nhau -(2^15) to 2^15 -1(sau này chúng ta sẽ xem làm thế nào để mở rộng ngay cả phạm vi để vượt qua phạm vi đã ký). Vì abphải tích cực họ phạm vi từ 0 to 2^15 - 1.

Chức năng ghép cặp :

Ánh xạ cho hai số nguyên được ký tối đa 16 bit (32767, 32767) sẽ là 2147418112, chỉ thiếu giá trị tối đa cho số nguyên 32 bit đã ký.

Bây giờ chức năng của Szudzik :

(32767, 32767) => 1073741823, nhỏ hơn nhiều ..

Hãy để tài khoản cho số nguyên âm. Đó là ngoài câu hỏi ban đầu tôi biết, nhưng chỉ cần xây dựng để giúp khách truy cập trong tương lai.

Chức năng ghép cặp :

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 là Int64. Đầu ra 64 bit cho đầu vào 16 bit có thể không được chấp nhận !!

Chức năng của 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 là 32 bit cho phạm vi không dấu hoặc 64 bit cho phạm vi đã ký, nhưng vẫn tốt hơn.

Bây giờ tất cả điều này trong khi đầu ra luôn luôn tích cực. Trong thế giới đã ký, sẽ còn tiết kiệm không gian hơn nữa nếu chúng ta có thể chuyển một nửa sản lượng sang trục âm . Bạn có thể làm điều đó như thế này cho 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

Những gì tôi làm: Sau khi áp dụng trọng số của 2các đầu vào và đi qua hàm, sau đó tôi chia số dư cho hai và lấy một số trong số chúng cho trục âm bằng cách nhân với -1.

Xem kết quả, đối với bất kỳ đầu vào nào trong phạm vi số 16bit đã ký , đầu ra nằm trong giới hạn của 32số nguyên bit đã ký là tuyệt vời. Tôi không chắc chắn cách thực hiện theo cách tương tự cho chức năng ghép nối Cantor nhưng không thử nhiều như nó không hiệu quả. Hơn nữa, nhiều tính toán liên quan đến chức năng ghép cặp Cantor cũng có nghĩa là nó cũng chậm hơn .

Đây là một triển khai 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;
}

Vì các tính toán trung gian có thể vượt quá giới hạn của 2Nsố nguyên đã ký, tôi đã sử dụng 4Nloại số nguyên (phép chia cuối cùng bằng cách 2đưa kết quả trở lại 2N).

Liên kết tôi đã cung cấp trên giải pháp thay thế mô tả độc đáo một biểu đồ của hàm sử dụng mọi điểm duy nhất trong không gian. Thật tuyệt vời khi thấy rằng bạn có thể mã hóa duy nhất một cặp tọa độ thành một số duy nhất có thể đảo ngược! Thế giới kỳ diệu của những con số !!


5
Hàm unash đã sửa đổi cho các số nguyên đã ký là gì?
Arets Paeglis

7
Câu trả lời này làm tôi bối rối. Nếu bạn muốn ánh xạ (0,0)thông qua (65535,65535)một số duy nhất, thì a<<16 + bvề cơ bản mọi cách sẽ tốt hơn (nhanh hơn, đơn giản hơn, dễ hiểu hơn, rõ ràng hơn) . Nếu bạn muốn (-32768,-32768)để (327687,327687)thay vào đó, những môn học 32768 đầu tiên.
BlueRaja - Daniel Pflughoeft

2
@ BlueRaja-DannyPflughoeft bạn đúng. Câu trả lời của tôi sẽ hợp lệ nếu phạm vi không giới hạn hoặc không xác định. Tôi sẽ cập nhật nó. Tôi đã viết nó trước khi giới hạn quan trọng với tôi. Chỉnh sửa câu trả lời này là từ lâu trong tâm trí của tôi. Thỉnh thoảng tôi sẽ tìm thấy thời gian.
nawfal

Liệu chức năng của Szudzik có hoạt động cho các kết hợp hoặc hoán vị. Có vẻ là hoán vị phải không? Nếu tôi muốn sử dụng cho Kết hợp, tôi có thể loại bỏ các phần IF và Else của thuật toán không?
Jamie Marshall

Dưới đây là cách triển khai Python của hàm Szudzik được khái quát thành các bộ dữ liệu có độ dài bất kỳ: gitlab.com/snippets/32559
Bác sĩ J

47

Nếu A và B có thể được biểu thị bằng 2 byte, bạn có thể kết hợp chúng trên 4 byte. Đặt A vào một nửa có ý nghĩa nhất và B trên một nửa có ý nghĩa nhất.

Trong ngôn ngữ C, điều này cho (giả sử sizeof (ngắn) = 2 và 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;
}

3
combine()nên return (unsigned short)(A<<16) | (unsigned short)(B); Vì vậy mà số âm có thể được đóng gói đúng cách.
Andy

2
@Andy A<<16sẽ đi ra khỏi giới hạn. Nó nên làreturn (unsigned int)(A<<16) | (unsigned short)(B);
DanSkeel

15

Điều này thậm chí có thể?
Bạn đang kết hợp hai số nguyên. Cả hai đều có phạm vi -2,147,483,648 đến 2,147,483,647 nhưng bạn sẽ chỉ thực hiện các tích cực. Điều đó làm cho 2147483647 ^ 2 = 4.61169E + 18 kết hợp. Vì mỗi kết hợp phải là duy nhất VÀ dẫn đến một số nguyên, bạn sẽ cần một số loại số nguyên ma thuật có thể chứa số lượng số này.

Hay logic của tôi là thiếu sót?


+1 Đó cũng là những gì tôi nghĩ (mặc dù tôi đã tính toán nói thứ tự của A và B không quan trọng)
lc.

4
Có logic của bạn là chính xác theo nguyên tắc pigeonhole. Thật không may người hỏi đã không xác định nếu số nguyên bị giới hạn hay không.
Không biết

Vâng, tôi cũng đã có suy nghĩ đó, nhưng tôi nghĩ rằng thông điệp về bản chất là giống nhau, vì vậy tôi đã không bận tâm đến việc tính toán lại.
Boris Callens

Ngoài ra tôi chỉ nhận ra rằng tôi nên chọn sách giáo khoa tính toán cơ hội (dịch nghĩa đen từ tiếng Hà Lan) một lần nữa.
Boris Callens

2
@Boris: Kansrekening là "lý thuyết xác suất".
Stephan202

8

Cách toán học tiêu chuẩn cho các số nguyên dương là sử dụng tính duy nhất của thừa số nguyên tố.

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

Nhược điểm là hình ảnh có xu hướng trải rộng trên một phạm vi số nguyên lớn, do đó, khi thể hiện ánh xạ trong thuật toán máy tính, bạn có thể gặp vấn đề với việc chọn loại phù hợp cho kết quả.

Bạn có thể sửa đổi điều này để đối phó với tiêu cực xybằng cách mã hóa một cờ có quyền hạn 5 và 7 điều khoản.

ví dụ

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

Toán học là tốt. Nhưng, như Boris nói, nếu bạn muốn chạy chương trình này như một chương trình máy tính, bạn phải tính đến độ chính xác của máy. Thuật toán sẽ hoạt động chính xác cho một tập hợp con của các số nguyên có thể biểu diễn trong máy có liên quan.
Yuval F

2
Tôi đã nói điều này trong đoạn thứ hai của tôi. Các thẻ trong câu hỏi chỉ ra 'thuật toán', 'toán học' và 'xác định', không phải bất kỳ ngôn ngữ cụ thể nào. Phạm vi đầu vào có thể không bị giới hạn và môi trường có thể có loại số nguyên không giới hạn 'bigint'.
CB Bailey

8

Đặt số alà số thứ nhất, bsố thứ hai. Hãy pa+1số nguyên tố thứ-thứ nhất, qb+1số nguyên tố thứ-thứ ba

Sau đó, kết quả là pq, nếu a<b,hoặc 2pqnếu a>b. Nếu a=b, hãy để nó được p^2.


4
Tôi nghi ngờ rằng bạn muốn có một giải pháp NP.
user44242

1
Điều này không tạo ra kết quả tương tự cho a = 5, b = 14 và a = 6, b = 15?
Lieven Keersmaekers

3
Hai sản phẩm của hai số nguyên tố khác nhau không thể có cùng kết quả (phân tách thừa số nguyên tố duy nhất) a = 5, b = 14 -> kết quả là 13 * 47 = 611 a = 6, b = 15 -> kết quả là 17 * 53 = 901
HỎI

4

Xây dựng bản đồ không khó lắm đâu:

   1 2 3 4 5 sử dụng ánh xạ này nếu (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 sử dụng ánh xạ này nếu (a, b) == (b, a) (gương)
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 sử dụng điều này nếu bạn cần tiêu cực / tích cực
 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

Tìm hiểu làm thế nào để có được giá trị cho a, b khó khăn hơn một chút.


4

f(a, b) = s(a+b) + a, Ở đâu s(n) = n*(n+1)/2

  • Đây là một chức năng - nó mang tính quyết định.
  • Nó cũng mang tính tiêm truyền - f ánh xạ các giá trị khác nhau cho các cặp (a, b) khác nhau. Bạn có thể chứng minh điều này bằng cách sử dụng thực tế : s(a+b+1)-s(a+b) = a+b+1 < a.
  • Nó trả về các giá trị khá nhỏ - tốt nếu bạn sẽ sử dụng nó để lập chỉ mục mảng, vì mảng không phải là lớn.
  • Nó thân thiện với bộ đệm - nếu hai cặp (a, b) gần nhau, thì f ánh xạ các số gần với nhau (so với các phương thức khác).

Tôi không hiểu ý của bạn là gì:

phải luôn luôn mang lại một số nguyên ở phía dương hoặc âm của số nguyên

Làm thế nào tôi có thể viết (lớn hơn), (ít hơn) ký tự trong diễn đàn này?


2
Lớn hơn và ít hơn nhân vật nên hoạt động tốt trong backtick escapes.
TRiG

Điều này tương đương với chức năng ghép nối Cantor và do đó không hoạt động với các số nguyên âm.
Davor Josipovic

4

Mặc dù câu trả lời của Stephan202 là câu trả lời thực sự duy nhất, nhưng đối với các số nguyên trong phạm vi giới hạn, bạn có thể làm tốt hơn. Ví dụ: nếu phạm vi của bạn là 0..10.000, thì bạn có thể làm:

#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));
}

Các kết quả có thể vừa với một số nguyên duy nhất cho một phạm vi lên tới căn bậc hai của số nguyên của loại số nguyên. Gói này hiệu quả hơn một chút so với phương pháp chung hơn của Stephan202. Nó cũng đơn giản hơn đáng kể để giải mã; không yêu cầu căn bậc hai, cho người mới bắt đầu :)


Đây có phải là bất kỳ cơ hội có thể cho phao?
Lukas


3

Kiểm tra điều này: http://en.wikipedia.org/wiki/Pigeonhole_principl . Nếu A, B và C cùng loại, không thể thực hiện được. Nếu A và B là số nguyên 16 bit và C là 32 bit, thì bạn chỉ cần sử dụng dịch chuyển.

Bản chất của các thuật toán băm là chúng không thể cung cấp một hàm băm duy nhất cho mỗi đầu vào khác nhau.


2

Đây là phần mở rộng mã của @DoctorJ cho các số nguyên không giới hạn dựa trên phương thức được đưa ra bởi @nawfal. Nó có thể mã hóa và giải mã. Nó hoạt động với các mảng bình thường và mảng 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

2

Làm thế nào về một cái gì đó đơn giản hơn nhiều: Cho hai số, A và B gọi str là phép nối: 'A' + ';' + 'B'. Sau đó để đầu ra là hàm băm (str). Tôi biết rằng đây không phải là một câu trả lời toán học, nhưng một kịch bản python đơn giản (có hàm băm dựng sẵn) sẽ thực hiện công việc.


2
nhưng (8,11) và (81,1) được ánh xạ tới cùng một số 811
Leevi L

Đó là một điểm tốt. Bạn có thể khắc phục vấn đề đó bằng cách chỉ cần thêm một biểu tượng ở giữa. Vì vậy, đối với (8, 11) băm chuỗi "8-11" và cho (81, 1) băm chuỗi "81-1". Vì vậy, nói chung cho (A, B) băm chuỗi "AB". (Tôi biết nó nghe có vẻ hacky, nhưng nó sẽ hoạt động).
Madhav Nakar

Điều này cũng sai vì nhiệm vụ đó là ánh xạ hai số nguyên sang một số nguyên mới, không phải là một chuỗi có ký hiệu
Leevi L

Tôi đến từ một quan điểm CS chứ không phải là một toán học (đối với các giải pháp toán học xem xét các phản ứng ở trên). Tôi đang lấy hai số nguyên, biến chúng thành một chuỗi, khi đó nó được chuyển thành một số nguyên. Về cơ bản, vâng tôi đang ánh xạ hai số nguyên sang một số nguyên mới.
Madhav Nakar

1

Những gì bạn đề nghị là không thể. Bạn sẽ luôn có va chạm.

Để ánh xạ hai đối tượng sang một tập hợp khác, tập hợp ánh xạ phải có kích thước tối thiểu của số lượng kết hợp dự kiến:

Giả sử số nguyên 32 bit, bạn có 2147483647 số nguyên dương. Chọn hai trong số các thứ tự không quan trọng và với sự lặp lại sẽ mang lại kết hợp 2305843008139952128. Điều này không phù hợp với bộ số nguyên 32 bit.

Tuy nhiên, bạn có thể phù hợp với ánh xạ này trong 61 bit. Sử dụng số nguyên 64 bit có lẽ là dễ nhất. Đặt từ cao thành số nguyên nhỏ hơn và từ thấp thành từ lớn hơn.


1

Giả sử bạn có số nguyên 32 bit, tại sao không chuyển A sang nửa 16 bit đầu tiên và B sang nửa kia?

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


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

Khác với điều này là không gian hiệu quả nhất có thể và rẻ tiền để tính toán, một hiệu ứng phụ thực sự thú vị là bạn có thể thực hiện toán học vectơ trên số đóng gói.

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

0

chúng ta có hai số B và C, mã hóa chúng thành một số A

A = B + C * N

Ở đâu

B = A% N = B

C = A / N = C


2
Làm thế nào để bạn chọn N để làm cho đại diện này độc đáo? Nếu bạn giải quyết vấn đề đó, câu trả lời này khác với những câu hỏi trên như thế nào?
Prune

Bạn nên thêm rằng N phải lớn hơn cả B và C.
Radoslav Stoyanov

0

Cho các số nguyên dương A và B, cho D = số chữ số A có và E = số chữ số B có kết quả có thể là một phép nối của D, 0, E, 0, A và B.

Ví dụ: A = 300, B = 12. D = 3, E = 2 result = 302030012. Điều này lợi dụng thực tế là số duy nhất bắt đầu bằng 0, là 0,

Pro: Dễ mã hóa, dễ giải mã, con người có thể đọc được, các chữ số có nghĩa có thể được so sánh trước, tiềm năng để so sánh mà không cần tính toán, kiểm tra lỗi đơn giản.

Nhược điểm: Kích thước của kết quả là một vấn đề. Nhưng không sao, tại sao chúng ta lại lưu trữ các số nguyên không giới hạn trong máy tính.


0

Nếu bạn muốn kiểm soát nhiều hơn như phân bổ các bit X cho số thứ nhất và bit Y cho số thứ hai, bạn có thể sử dụng mã nà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);
    }
};

Tôi sử dụng tổng cộng 32 bit. Ý tưởng ở đây là nếu bạn muốn ví dụ số đầu tiên sẽ lên tới 10 bit và số thứ hai sẽ lên tới 12 bit, bạn có thể làm điều này:

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

Bây giờ bạn có thể lưu trữ num_avới số lượng tối đa 2^10 - 1 = 1023num_bgiá trị tối đa của 2^12 - 1 = 4095.

Để đặt giá trị cho num A và num B:

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

Bây giờ bnumlà tất cả các bit (tổng cộng 32 bit. Bạn có thể sửa đổi mã để sử dụng 64 bit) Để lấy num a:

int a = nums_mapper.GetNumA(bnum);

Để lấy số b:

int b = nums_mapper.GetNumB(bnum);

EDIT: bnumcó thể được lưu trữ bên trong lớp. Tôi đã không làm điều đó bởi vì nhu cầu của riêng tôi, tôi đã chia sẻ mã và hy vọng rằng nó sẽ hữu ích.

Cảm ơn nguồn: https://www.geekforgeek.org/extract-k-bits-given-poseition-number/ cho chức năng trích xuất bit và cũng cảm ơn để mouvicieltrả lời trong bài đăng này. Sử dụng chúng cho các nguồn tôi có thể tìm ra giải pháp tiên tiến hơn

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.