Saya mengalami masalah teoretis yang menarik beberapa tahun yang lalu. Saya tidak pernah menemukan solusi, dan itu terus menghantui saya ketika saya tidur.
Misalkan Anda memiliki aplikasi (C #) yang menyimpan sejumlah angka di int, disebut x. (Nilai x tidak tetap). Ketika program dijalankan, x dikalikan dengan 33 dan kemudian ditulis ke file.
Kode sumber dasar terlihat seperti ini:
int x = getSomeInt();
x = x * 33;
file.WriteLine(x); // Writes x to the file in decimal format
Beberapa tahun kemudian, Anda menemukan bahwa Anda membutuhkan nilai asli X kembali. Beberapa perhitungan sederhana: Cukup bagi angka dalam file dengan 33. Namun, dalam kasus lain, X cukup besar sehingga perkalian menyebabkan integer overflow. Menurut dokumen , C # akan memotong bit orde tinggi hingga jumlahnya kurang dari int.MaxValue
. Apakah mungkin, dalam hal ini, untuk:
- Pulihkan X itu sendiri atau
- Pulihkan daftar nilai yang mungkin untuk X?
Menurut saya (walaupun logika saya tentu saja cacat) bahwa satu atau keduanya harus dimungkinkan, karena kasus penambahan yang lebih sederhana bekerja (Pada dasarnya jika Anda menambahkan 10 ke X dan membungkus, Anda dapat mengurangi 10 dan berakhir dengan X lagi ) dan perkalian adalah penambahan yang hanya berulang. Juga membantu (saya percaya) adalah fakta bahwa X dikalikan dengan nilai yang sama dalam semua kasus - konstan 33.
Ini telah menari di sekitar tengkorak saya pada saat-saat aneh selama bertahun-tahun. Itu akan terpikir oleh saya, saya akan meluangkan waktu mencoba memikirkannya, dan kemudian saya akan melupakannya selama beberapa bulan. Saya lelah mengejar masalah ini! Adakah yang bisa menawarkan wawasan?
(Catatan: Saya benar-benar tidak tahu cara memberi tag pada yang ini. Saran diterima.)
Sunting: Izinkan saya mengklarifikasi bahwa jika saya bisa mendapatkan daftar nilai yang mungkin untuk X, ada tes lain yang bisa saya lakukan untuk membantu saya mempersempitnya ke nilai asli.
m
hanya 2 ^ 32 atau 2 ^ 64, ditambah eksponensiala
modulom
sangat mudah (abaikan saja luapan di sana)r*s^-1 mod m
dan Anda perlu untuk menemukan keduar
dans
. Di sini, kita milikir*s mod m
dan kita tahu segalanya kecualir
.Jawaban:
Kalikan dengan 1041204193.
Ketika hasil perkalian tidak cocok dengan int, Anda tidak akan mendapatkan hasil yang tepat, tetapi Anda akan mendapatkan angka yang setara dengan hasil yang tepat modulo 2 ** 32 . Itu berarti bahwa jika angka yang Anda kalikan dengan coprime menjadi 2 ** 32 (yang berarti angka itu harus ganjil), Anda dapat mengalikan dengan pembalikan multiplikatifnya untuk mendapatkan nomor Anda kembali. Wolfram Alpha atau algoritma Euclidean yang diperluas dapat memberi tahu kami modul multiplikatif inversi 33-an 33-an ** 32 adalah 1041204193. Jadi, kalikan dengan 1041204193, dan Anda memiliki x kembali yang asli.
Jika kita memiliki, katakanlah, 60 bukannya 33, kita tidak akan dapat memulihkan nomor aslinya, tetapi kita akan dapat mempersempitnya ke beberapa kemungkinan. Dengan memfaktorkan 60 menjadi 4 * 15, menghitung kebalikan dari 15 mod 2 ** 32, dan mengalikannya, kita dapat memulihkan 4 kali dari angka aslinya, hanya menyisakan 2 bit orde tinggi dari angka tersebut menjadi brute-force. Wolfram Alpha memberi kami 4008636143 untuk kebalikannya, yang tidak sesuai dengan int, tapi tidak apa-apa. Kami hanya menemukan angka yang setara dengan 4008636143 mod 2 ** 32, atau memaksanya ke int agar kompiler melakukan itu untuk kami, dan hasilnya juga akan menjadi kebalikan dari 15 mod 2 ** 32. ( Kami mendapatkan -286331153. )
sumber
Ini mungkin lebih cocok sebagai pertanyaan untuk Matematika (s) SE. Anda pada dasarnya berurusan dengan modular aritmatika, karena menjatuhkan bit paling kiri adalah hal yang sama.
Saya tidak sebagus Matematika seperti halnya orang-orang yang ada di Matematika (sic) SE, tetapi saya akan mencoba menjawab.
Yang kami miliki di sini adalah bahwa jumlahnya dikalikan dengan 33 (3 * 11), dan satu-satunya penyebut yang umum dengan mod Anda adalah 1. Itu karena menurut definisi bit di komputer adalah kekuatan dua, dan dengan demikian mod Anda adalah beberapa kekuatan dua.
Anda akan dapat membangun tabel di mana untuk setiap nilai sebelumnya Anda menghitung nilai berikut. Dan pertanyaannya adalah apakah angka-angka berikut hanya sesuai dengan yang sebelumnya.
Jika bukan 33, tetapi prima atau kekuatan prima, saya percaya bahwa jawabannya adalah ya, tetapi dalam hal ini ... tanyakan pada Math.SE!
Tes terprogram
Ini di C ++ karena saya tidak tahu C #, tetapi konsepnya masih berlaku. Ini tampaknya menunjukkan bahwa Anda dapat:
Setelah mengisi peta seperti itu, Anda akan selalu bisa mendapatkan X sebelumnya jika Anda tahu yang berikutnya. Hanya ada satu nilai setiap saat.
sumber
maxval+1
0 hanya untuk tipe yang tidak ditandatangani.Salah satu cara untuk mendapatkannya adalah dengan menggunakan brute force. Maaf saya tidak tahu C # tetapi yang berikut adalah kode pseudo seperti-c untuk menggambarkan solusi:
Secara teknis, yang Anda butuhkan hanyalah
x*33%(INT_MAX+1) == test_value
integer overflow secara otomatis akan melakukan%
operasi untuk Anda kecuali bahasa Anda menggunakan integer presisi sewenang-wenang (bigint).Apa yang Anda dapatkan di sini adalah serangkaian angka yang mungkin merupakan angka asli. Angka pertama yang dicetak adalah angka yang menghasilkan satu putaran luapan. Angka kedua akan menjadi angka yang akan menghasilkan dua putaran overflow. Dan seterusnya..
Jadi, jika Anda tahu data Anda lebih baik, Anda bisa membuat tebakan yang lebih baik. Sebagai contoh, matematika jam umum (meluap setiap jam 12) cenderung membuat angka pertama lebih mungkin karena kebanyakan orang tertarik pada hal-hal yang terjadi hari ini.
sumber
int
bilangan bulat bertanda 4 byte yang membungkus, jadi jawaban Anda masih bagus, meskipun memaksa kasar tidak akan menjadi cara terbaik untuk pergi jika Anda memiliki banyak input! :)int
tidak dijamin untuk membungkus (lihat dokumen kompiler Anda). Itu berlaku untuk tipe yang tidak ditandatangani.Anda dapat memecahkan SMT Z3 untuk memintanya memberi Anda tugas yang memuaskan untuk formula tersebut
x * 33 = valueFromFile
. Ini akan membalikkan persamaan itu untuk Anda dan memberi Anda semua nilai yang mungkin darix
. Z3 mendukung aritmatika bitvektor yang tepat termasuk perkalian.Output terlihat seperti ini:
sumber
Untuk membatalkan hasil itu akan memberi Anda jumlah angka terbatas non-nol (normalnya tak terbatas, tetapi
int
adalah himpunan bagian terbatas ℤ). Jika ini dapat diterima, hasilkan saja angkanya (lihat jawaban lain).Kalau tidak, Anda perlu mempertahankan daftar riwayat (panjang terbatas atau tak terbatas) dari sejarah variabel.
sumber
Seperti biasa, ada solusi dari seorang ilmuwan dan solusi dari seorang insinyur.
Di atas Anda akan menemukan solusi yang sangat baik dari seorang ilmuwan, yang selalu berhasil, tetapi mengharuskan Anda untuk menghitung "invers multiplikatif".
Ini adalah solusi cepat dari insinyur, yang tidak akan memaksa Anda untuk mencoba semua bilangan bulat yang mungkin.
Apa idenya?
Int -> Long
)Int.MaxValue * multiplier
Kode yang dapat dieksekusi penuh terletak di http://ideone.com/zVMbGV
Detail:
val problemAsLong = (-1947051863).toLong & 0xFFFFFFFFL
Di sini kita mengonversi nomor yang disimpan ke Long, tetapi karena Int dan Long ditandatangani, kita harus melakukannya dengan benar.
Jadi kami membatasi jumlah menggunakan bitwise DAN dengan bit Int.
val overflowBit = 0x100000000L
Penggandaan atau penggandaan ini bisa hilang dengan penggandaan awal.
Ini sedikit pertama di luar rentang Int.
for(test <- 0 until multiplier)
Menurut Ide ke-3, luapan maksimal dibatasi oleh pengganda, jadi jangan mencoba lebih dari yang sebenarnya kita butuhkan.
if((problemAsLong + overflowBit * test) % multiplier == 0)
Periksa apakah dengan menambahkan kemungkinan overflow yang hilang, kami menemukan solusi
val original = originalLong.toInt
Masalah aslinya ada di kisaran Int, jadi mari kita kembali ke sana. Kalau tidak, kita bisa salah memulihkan nomor, yang negatif.
println(s"$original (test = $test)")
Jangan merusak solusi pertama, karena mungkin ada solusi lain yang mungkin.
PS: Gagasan ke-3 tidak sepenuhnya benar, tetapi dibiarkan begitu agar bisa dimengerti.
Int.MaxValue
adalah0x7FFFFFFF
, tetapi overflow maksimal adalah0xFFFFFFFF * multiplier
.Jadi teks yang benar adalah "Overflow tidak lebih dari
-1 * multiplier
".Ini benar, tetapi tidak semua orang akan memahaminya.
sumber