การทำแผนที่จำนวนเต็มสองจำนวนกับหนึ่งในวิธีที่ไม่ซ้ำกันและกำหนดไว้


235

ลองนึกภาพจำนวนเต็มบวกสองตัว A และ B ฉันต้องการรวมสองตัวนี้เป็นจำนวนเต็ม C เดียว

จะไม่มีจำนวนเต็มอื่น D และ E รวมกับ C ดังนั้นการรวมเข้ากับตัวดำเนินการเพิ่มไม่ทำงาน เช่น 30 + 10 = 40 = 40 + 0 = 39 + 1 ไม่สามารถต่อการทำงานร่วมกันได้ เช่น "31" + "2" = 312 = "3" + "12"

การดำเนินการรวมกันนี้ควรกำหนดไว้ (เสมอให้ผลลัพธ์เดียวกันกับอินพุตเดียวกัน) และควรให้จำนวนเต็มทั้งด้านบวกหรือด้านลบของจำนวนเต็ม


10
คุณควรชี้แจงหากคุณหมายถึงจำนวนเต็มในซอฟต์แวร์หรือจำนวนเต็มในคณิตศาสตร์ ในซอฟต์แวร์คุณเลือกประเภทจำนวนเต็มใด ๆ และมันจะมีขนาดดังนั้นคุณจึงมีจำนวน จำกัด ดังนั้นจึงไม่มีวิธีแก้ปัญหา (เว้นแต่แน่นอนว่าข้อมูลอินพุตของคุณรับประกันว่าอยู่ในช่วงและสามารถส่งออกของคุณได้ จำนวนเต็มใด ๆ ) ในวิชาคณิตศาสตร์ดูวิธีแก้ปัญหาของ ASK
Daniel Daranas

ฉันกำลังพูดถึงจำนวนเต็มที่มีขอบเขตในช่วงที่ต่ำและเป็นบวก พูด 0 ถึง 10,000
อันตรายต่อ

27
@harm: งั้นแค่นั้น10,001*A + Bล่ะ
BlueRaja - Danny Pflughoeft

2
ฉันพบฟังก์ชัน PHP นี้แล้ว: gist.github.com/hannesl/8031402
cakan

หากคำสั่งซื้อไม่สำคัญเช่น: (3,12) & (12,3) ให้ผลเหมือนกันฉันใช้ "A + B" + "A * B"
Sodj

คำตอบ:


233

คุณกำลังมองหา bijective NxN -> Nการทำแผนที่ เหล่านี้จะใช้สำหรับเช่นการประกบกัน มีลักษณะที่รูปแบบไฟล์ PDF นี้สำหรับการแนะนำให้เรียกว่าฟังก์ชั่นการจับคู่ Wikipedia แนะนำฟังก์ชั่นการจับคู่ที่เฉพาะเจาะจงคือฟังก์ชั่นการจับคู่คันทอร์ :

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

สามข้อสังเกต:

  • ตามที่คนอื่นมีความชัดเจนหากคุณวางแผนที่จะใช้ฟังก์ชั่นการจับคู่คุณอาจพบว่าคุณต้องการจำนวนเต็มขนาดใหญ่ (bignums) โดยพลการ
  • หากคุณไม่ต้องการแยกความแตกต่างระหว่างคู่ (a, b) และ (b, a) ให้เรียงลำดับ a และ b ก่อนที่จะใช้ฟังก์ชันการจับคู่
  • ที่จริงฉันโกหก คุณกำลังมองหาการZxZ -> Nทำแผนที่bijective ฟังก์ชั่นของคันทอร์ทำงานได้เฉพาะกับตัวเลขที่ไม่เป็นลบเท่านั้น อย่างไรก็ตามนี่ไม่ใช่ปัญหาเนื่องจากง่ายต่อการกำหนด bijection f : Z -> Nเช่น:
    • f (n) = n * 2ถ้า n> = 0
    • f (n) = -n * 2 - 1ถ้า n <0

13
+1 ฉันคิดว่านี่เป็นคำตอบที่ถูกต้องสำหรับจำนวนเต็มที่ไม่ได้ จำกัด
ไม่ทราบ

4
ฉันจะรับค่า k1 อีกครั้งได้อย่างไร
MinuMaster

3
@MinuMaster: ที่อธิบายไว้ในบทความวิกิพีเดียเดียวกันภายใต้Inverting ฟังก์ชั่นการจับคู่ต้นเสียง
Stephan202

4
ดูฟังก์ชั่นของ Szudzik ซึ่งอธิบายโดย newfal ด้านล่าง
OliJG

1
แม้ว่าสิ่งนี้จะถูกต้องสำหรับจำนวนเต็มที่ไม่ได้ จำกัด แต่ก็ไม่เหมาะสำหรับจำนวนเต็มที่มีขอบเขต ฉันคิดว่าความคิดเห็นของ @ blue-raja นั้นสมเหตุสมผลที่สุดแล้ว
Kardasis

226

ฟังก์ชั่นการจับคู่ต้นเสียงมันเป็นหนึ่งในคนที่ดีกว่าออกมีการพิจารณาที่เรียบง่ายรวดเร็วและพื้นที่ที่มีประสิทธิภาพ แต่มีบางสิ่งที่ดียิ่งขึ้นเผยแพร่ในวุลแฟรมโดยแมทธิว Szudzik นี่ ข้อ จำกัด ของฟังก์ชั่นการจับคู่คันทอร์ (ค่อนข้าง) คือช่วงของผลลัพธ์ที่เข้ารหัสไม่ได้อยู่ภายในขีด จำกัด ของ2Nจำนวนเต็มบิตเสมอถ้าอินพุตเป็นNจำนวนเต็มสองบิต นั่นคือถ้าอินพุตของฉันเป็น16จำนวนเต็มสองบิตตั้งแต่0 to 2^16 -1นั้นจะมีการ2^16 * (2^16 -1)รวมกันของอินพุตดังนั้นด้วยหลักการ 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(ต่อมาเราจะเห็นวิธีการขยายแม้ ouput ตั้งแต่aและจะต้องมีการบวกที่พวกเขามีตั้งแต่b0 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ปัจจัยการผลิตและจะผ่านฟังก์ชั่นที่ผมแล้วแบ่ง ouput -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)

ลิงก์ที่ฉันให้ไว้ในโซลูชันทางเลือกแสดงกราฟของฟังก์ชันที่ใช้ทุกจุดในอวกาศอย่างชัดเจน มันวิเศษมากที่เห็นว่าคุณสามารถเข้ารหัสคู่ของพิกัดเป็นตัวเลขเดียวกลับด้านได้! โลกมหัศจรรย์ของตัวเลข !!


5
อะไรคือฟังก์ชั่น unhash ที่แก้ไขสำหรับจำนวนเต็มที่ลงนามแล้ว?
Arets Paeglis

7
คำตอบนี้ทำให้ฉันสับสน หากคุณต้องการแผนที่(0,0)ผ่าน(65535,65535)ไปยังหมายเลขเดียวแล้วa<<16 + bจะดีกว่าในทุกวิถีทางพื้น(เร็วขึ้นง่ายง่ายต่อการเข้าใจชัดเจนมากขึ้น) หากคุณต้องการ(-32768,-32768)ที่จะ(327687,327687)แทนเพียงเรื่องแรก 32768
BlueRaja - Danny Pflughoeft

2
@ BlueRaja-DannyPflughoeft คุณพูดถูก คำตอบของฉันจะถูกต้องหากช่วงไม่ จำกัด หรือไม่ทราบ ฉันจะอัปเดต ฉันได้เขียนไว้ก่อนถึงขีด จำกัด สำคัญสำหรับฉัน การแก้ไขคำตอบนี้อยู่ในใจของฉันมานานแล้ว ฉันจะหาเวลาเร็ว ๆ นี้
nawfal

ฟังก์ชั่นของ Szudzik ทำงานเพื่อการผสมหรือการเรียงสับเปลี่ยน ดูเหมือนจะเป็นวิธีเรียงสับเปลี่ยนใช่มั้ย หากฉันต้องการใช้สำหรับชุดค่าผสมฉันสามารถกำจัดส่วน IF และอื่น ๆ ของอัลกอริทึมได้หรือไม่
เจมี่มาร์แชลล์

นี่คือการใช้งาน Python ของฟังก์ชั่นของ Szudzik โดยทั่วไปเพื่อความยาวของ tuples: gitlab.com/snippets/32559
Doctor J

47

หาก A และ B สามารถแสดงด้วย 2 ไบต์คุณสามารถรวมไว้ใน 4 ไบต์ วาง A บนครึ่งที่สำคัญที่สุดและ B บนครึ่งที่สำคัญน้อยที่สุด

ในภาษา C สิ่งนี้จะให้ (สมมติว่า sizeof (สั้น) = 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;
}

3
combine()ควรreturn (unsigned short)(A<<16) | (unsigned short)(B); เพื่อให้ตัวเลขติดลบสามารถบรรจุได้อย่างถูกต้อง
Andy

2
@Andy A<<16จะไปจากขอบเขต มันควรเป็นreturn (unsigned int)(A<<16) | (unsigned short)(B);
DanSkeel

15

เป็นไปได้ไหม
คุณกำลังรวมจำนวนเต็มสองจำนวน พวกเขาทั้งสองมีช่วง -2,147,483,648 ถึง 2,147,483,647 แต่คุณจะได้รับผลบวกเท่านั้น นั่นทำให้ 2147483647 ^ 2 = 4,61169E + 18 ชุดค่าผสม เนื่องจากชุดค่าผสมแต่ละชุดจะต้องไม่ซ้ำกันและส่งผลให้มีจำนวนเต็มคุณจะต้องมีจำนวนเต็มวิเศษที่สามารถมีจำนวนจำนวนนี้

หรือเหตุผลของฉันมีข้อบกพร่อง?


+1 นั่นคือสิ่งที่ฉันคิดเช่นกัน (แม้ว่าฉันจะทำการคำนวณโดยบอกว่าคำสั่งของ A และ B ไม่สำคัญ)
lc

4
ใช่ตรรกะของคุณถูกต้องโดยหลักการของนกพิราบ โดยไม่ถามผู้ถามไม่ได้ระบุว่าจำนวนเต็มที่ถูกผูกไว้หรือไม่
ไม่ทราบ

ใช่ฉันเคยคิดแบบนั้นเหมือนกัน แต่ฉันคิดว่าข้อความนั้นมีความสำคัญเหมือนกันดังนั้นฉันจึงไม่ต้องนึกถึงอีกเลย
Boris Callens

นอกจากนี้ฉันเพิ่งตระหนักว่าฉันควรรับหนังสือการคำนวณโอกาส (แปลตามตัวอักษรจากภาษาดัตช์) อีกครั้ง
Boris Callens

2
@ Boris: Kansrekening คือ "ทฤษฎีความน่าจะเป็น"
Stephan202

8

วิธีการทางคณิตศาสตร์มาตรฐานสำหรับจำนวนเต็มบวกคือการใช้เอกลักษณ์ของการแยกตัวประกอบเฉพาะ

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

ข้อเสียคือภาพมีแนวโน้มที่จะขยายจำนวนเต็มค่อนข้างมากดังนั้นเมื่อแสดงการแมปในอัลกอริทึมคอมพิวเตอร์คุณอาจมีปัญหากับการเลือกประเภทที่เหมาะสมสำหรับผลลัพธ์

คุณสามารถแก้ไขสิ่งนี้เพื่อจัดการกับค่าลบxและyโดยการเข้ารหัสค่าสถานะที่มีกำลัง 5 และ 7 คำ

เช่น

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

คณิตศาสตร์ดี แต่อย่างที่ Boris กล่าวถ้าคุณต้องการเรียกใช้โปรแกรมคอมพิวเตอร์คุณต้องคำนึงถึงความละเอียดของเครื่อง อัลกอริทึมจะทำงานอย่างถูกต้องสำหรับชุดย่อยของจำนวนเต็มที่แสดงในเครื่องที่เกี่ยวข้อง
Yuval F

2
ฉันระบุไว้ในวรรคที่สอง แท็กของคำถามระบุ 'อัลกอริทึม', 'คณิตศาสตร์' และ 'กำหนดขึ้น' ไม่ใช่ภาษาใดภาษาหนึ่ง ช่วงอินพุตอาจไม่ จำกัด และสภาพแวดล้อมอาจมีประเภทจำนวนเต็ม 'bigint' ที่ไม่มีขอบเขต
CB Bailey

8

ให้ตัวเลขaเป็นตัวแรก, bตัวที่สอง อนุญาตpเป็นa+1จำนวนเฉพาะ -th qเป็นb+1จำนวนเฉพาะ -th

แล้วผลที่ได้คือpqถ้าa<b,หรือถ้า2pq a>bถ้าให้มันเป็นa=bp^2


4
ฉันสงสัยว่าคุณต้องการโซลูชัน NP
user44242

1
สิ่งนี้ไม่ให้ผลลัพธ์ที่เหมือนกันสำหรับ a = 5, b = 14 และ a = 6, b = 15 หรือไม่
Lieven Keersmaekers

3
ผลิตภัณฑ์สองช่วงที่แตกต่างกันสองช่วงไม่สามารถมีผลลัพธ์เหมือนกัน (การแยกตัวประกอบเฉพาะนายกรัฐมนตรี) a = 5, b = 14 -> ผลลัพธ์คือ 13 * 47 = 611 a = 6, b = 15 -> ผลลัพธ์คือ 17 * 53 = 901
ถาม

4

การสร้างแผนที่ไม่ใช่เรื่องยาก:

   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 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 14
4 4 8 11 15 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 14
 2 4 8 11 15 19 19
-2 6 10 14 19 24

การหาวิธีหาค่าสำหรับ a, b นั้นยากกว่านิดหน่อย


4

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 จับคู่หมายเลขกับคู่ที่อยู่ใกล้กัน (เทียบกับวิธีอื่น)

ฉันไม่เข้าใจสิ่งที่คุณหมายถึงโดย:

ควรให้จำนวนเต็มทั้งด้านบวกหรือด้านลบของจำนวนเต็ม

ฉันจะเขียนตัวอักษร (มากกว่า), (น้อยกว่า) ตัวในฟอรัมนี้ได้อย่างไร?


2
backtick escapesมากกว่าและน้อยกว่าตัวละครควรปรับการทำงานภายใน
TRiG

นี่เทียบเท่ากับฟังก์ชั่นการจับคู่คันทอร์และไม่สามารถใช้กับจำนวนเต็มลบได้
Davor Josipovic

4

แม้ว่าคำตอบของ 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));
}

ผลลัพธ์สามารถใส่ได้ในจำนวนเต็มเดียวสำหรับช่วงสูงสุดถึงสแควร์รูทของ cardinality ของประเภทจำนวนเต็ม ชุดนี้มีประสิทธิภาพมากกว่าวิธีทั่วไปของ Stephan202 เล็กน้อย มันง่ายกว่ามากในการถอดรหัส; ไม่ต้องใช้รากที่สองสำหรับ starters :)


เป็นไปได้ไหมที่มีโอกาสเป็นไปได้สำหรับการลอยตัว?
ลูคัส

4

สำหรับจำนวนเต็มบวกเป็นอาร์กิวเมนต์และตำแหน่งอาร์กิวเมนต์ไม่สำคัญ:

  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>
    

3

ตรวจสอบเรื่องนี้: http://en.wikipedia.org/wiki/Pigeonhole_principle ถ้า A, B และ C เป็นประเภทเดียวกันมันไม่สามารถทำได้ หาก A และ B เป็นจำนวนเต็ม 16 บิตและ C เป็น 32 บิตจากนั้นคุณสามารถใช้การเลื่อน

ธรรมชาติของอัลกอริทึมการแปลงแป้นพิมพ์คือพวกเขาไม่สามารถให้แฮชที่ไม่ซ้ำกันสำหรับแต่ละอินพุตที่แตกต่างกัน


2

นี่คือส่วนขยายของรหัส @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'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

สิ่งที่ง่ายกว่ามาก: ด้วยตัวเลขสองตัว A และ B ปล่อยให้มีการต่อกันเป็น 'A' + ';' + 'B' จากนั้นให้เอาต์พุตเป็น hash (str) ฉันรู้ว่านี่ไม่ใช่คำตอบทางคณิตศาสตร์ แต่สคริปต์ python (ซึ่งมีฟังก์ชันแฮชในตัว) ควรทำงาน


2
แต่ (8,11) และ (81,1) ถูกแมปไปยังหมายเลขเดียวกัน 811
Leevi L

นั่นเป็นจุดที่ดี คุณสามารถแก้ไขปัญหานั้นได้โดยเพียงแค่เพิ่มสัญลักษณ์ตรงกลาง ดังนั้นสำหรับ (8, 11) แฮชสตริง "8-11" และ (81, 1) แฮชสตริง "81-1" ดังนั้นโดยทั่วไปสำหรับ (A, B) แฮชสตริง "AB" (ฉันรู้ว่ามันฟังดูแฮ็ค แต่ควรใช้งานได้)
Madhav Nakar

ยังผิดเพราะงานที่จะ map จำนวนเต็มสองจำนวนที่จะเป็นจำนวนเต็มใหม่ไม่สตริงที่มีสัญลักษณ์เป็น
Leevi L

ฉันมาจากมุมมอง CS แทนที่จะเป็นเชิงคณิตศาสตร์ (สำหรับโซลูชันทางคณิตศาสตร์ดูคำตอบด้านบน) ฉันกำลังรับจำนวนเต็มสองจำนวนทำให้เป็นสตริงเมื่อนั้นกลายเป็นจำนวนเต็ม โดยพื้นฐานแล้วใช่ฉันกำลังจับคู่สองจำนวนเต็มกับใหม่
Madhav Nakar

1

สิ่งที่คุณแนะนำเป็นไปไม่ได้ คุณจะมีการชนกันเสมอ

ในการจับคู่วัตถุสองชุดกับอีกชุดหนึ่งชุดที่แมปต้องมีขนาดขั้นต่ำของจำนวนชุดค่าผสมที่คาดไว้:

สมมติว่าเป็นจำนวนเต็ม 32 บิตคุณมีจำนวนเต็มบวก 2147483647 การเลือกสองรายการเหล่านี้ที่คำสั่งซื้อไม่สำคัญและด้วยการรวมกันซ้ำ ๆ จะทำให้ได้ชุดแบบคู่กัน สิ่งนี้ไม่พอดีในชุดของจำนวนเต็ม 32- บิต

อย่างไรก็ตามคุณสามารถปรับการแมปนี้ให้เป็น 61 บิตได้ การใช้จำนวนเต็ม 64 บิตอาจเป็นวิธีที่ง่ายที่สุด ตั้งค่าคำสูงเป็นจำนวนเต็มขนาดเล็กและคำต่ำถึงค่าที่มากกว่า


1

สมมติว่าคุณมีจำนวนเต็ม 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

0

ขอให้เรามีหมายเลข B และ C สองตัวโดยเข้ารหัสเป็นตัวเลข A ตัวเดียว

A = B + C * N

ที่ไหน

B = A% N = B

C = A / N = C


2
คุณจะเลือก N เพื่อทำให้การแสดงนี้ไม่ซ้ำได้อย่างไร หากคุณแก้ปัญหาดังกล่าวคำตอบนี้แตกต่างจากที่กล่าวไว้ข้างต้นอย่างไร
พรุน

คุณควรเพิ่ม N ที่ต้องมากกว่า B และ C
Radoslav Stoyanov

0

รับจำนวนเต็มบวก A และ B ให้ D = จำนวนของหลัก A มีและ E = จำนวนหลัก B มีผลที่ได้คือการต่อกันของ D, 0, E, 0, A และ B

ตัวอย่าง: A = 300, B = 12. D = 3, E = 2 ผลลัพธ์ = 302030012 สิ่งนี้ใช้ประโยชน์จากความจริงที่ว่าจำนวนเฉพาะที่ขึ้นต้นด้วย 0 คือ 0

Pro: ง่ายต่อการเข้ารหัส, ง่ายต่อการถอดรหัส, มนุษย์สามารถอ่านได้, เลขนัยสำคัญสามารถเปรียบเทียบได้ก่อน, มีความเป็นไปได้ที่จะเปรียบเทียบโดยไม่ต้องคำนวณ, ตรวจสอบข้อผิดพลาดอย่างง่าย

ข้อด้อย: ขนาดของผลลัพธ์เป็นปัญหา แต่ก็ไม่เป็นไรทำไมเราถึงเก็บจำนวนเต็มไว้ในคอมพิวเตอร์ไม่ได้


0

หากคุณต้องการการควบคุมเพิ่มเติมเช่นจัดสรรบิต 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ค่า naximum ของ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:

int a = nums_mapper.GetNumA(bnum);

ในการรับ NUM b:

int b = nums_mapper.GetNumB(bnum);

แก้ไข: bnumสามารถเก็บไว้ในชั้นเรียน ฉันไม่ได้ทำเพราะความต้องการของตัวเองฉันแชร์รหัสและหวังว่าจะเป็นประโยชน์

ขอบคุณสำหรับแหล่งที่มา: https://www.geeksforgeeks.org/extract-k-bits-given-position-number/ สำหรับฟังก์ชั่นในการแยกบิตและขอบคุณที่mouvicielตอบในโพสต์นี้ การใช้สิ่งเหล่านี้กับแหล่งข้อมูลฉันสามารถหาวิธีแก้ปัญหาขั้นสูงเพิ่มเติมได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.