Perkiraan Pembagi Umum Tercepat

13

Gambaran

Dalam tantangan ini, Anda akan diberikan dua angka yang keduanya merupakan offset kecil yang lebih besar dari kelipatan angka berukuran sedang. Anda harus menampilkan nomor berukuran sedang yang hampir merupakan pembagi kedua angka, kecuali untuk offset kecil.

Ukuran angka yang terlibat akan diparameterisasi dengan parameter kesulitan l,. Tujuan Anda adalah memecahkan masalah sebesar mungkin ldalam waktu kurang dari 1 menit.


Mempersiapkan

Dalam masalah yang diberikan, akan ada nomor rahasia p,, yang akan menjadi nomor bit acak l^2( l*l). Akan ada dua pengganda,, q1, q2yang akan menjadi l^3angka bit acak , dan akan ada dua offset r1, r2,, yang akan menjadi langka bit acak .

Input untuk program Anda akan x1, x2, didefinisikan sebagai:

x1 = p * q1 + r1
x2 = p * q2 + r2

Berikut adalah program untuk menghasilkan kasus uji, dengan Python:

from random import randrange
from sys import argv

l = int(argv[1])

def randbits(bits):
    return randrange(2 ** (bits - 1), 2 ** bits)

p = randbits(l ** 2)
print(p)
for i in range(2):
    q_i = randbits(l ** 3)
    r_i = randbits(l)
    print(q_i * p + r_i)

Baris pertama dari output adalah solusi yang memungkinkan, sedangkan baris kedua dan ketiga adalah input yang akan diberikan oleh program Anda.


Program Anda

Mengingat x1, x2dan l, Anda harus menemukan l^2sejumlah bit p'sehingga x1 % p'dan x2 % p'keduanya lnomor bit. pakan selalu berfungsi, meskipun mungkin ada kemungkinan lain. Berikut fungsi untuk memverifikasi solusi:

def is_correct(x1, x2, l, p_prime):
    p_prime_is_good = p_prime >> (l**2 - 1) and not p_prime >> l ** 2
    x1_is_good = (x1 % p_prime) >> (l-1) and not (x1 % p_prime) >> l
    x2_is_good = (x2 % p_prime) >> (l-1) and not (x2 % p_prime) >> l
    return bool(p_prime_is_good and x1_is_good and x2_is_good)

Contoh

Misalkan ladalah 3. Program generator memilih angka 9-bit p, yang dalam hal ini adalah 442. Generator memilih dua 3angka bit r1, r2, yaitu 4, 7. Generator memilih dua 27angka bit q1, q2, yaitu 117964803, 101808039. Karena pilihan-pilihan ini, x1, x2adalah 52140442930, 44999153245.

Program Anda akan diberikan 52140442930, 44999153245sebagai input, dan harus menampilkan angka 9-bit (dalam kisaran [256, 511]) sehingga 52140442930dan 44999153245modulo angka tersebut memberikan angka 3 bit (dalam kisaran [4, 7]). 442adalah satu-satunya nilai dalam kasus ini, jadi program Anda harus menampilkannya 442.


Lebih banyak contoh

l = 2
x1 = 1894
x2 = 2060
p = 11
No other p'.

l = 3
x1 = 56007668599
x2 = 30611458895
p = 424
No other p'.

l = 6
x1 = 4365435975875889219149338064474396898067189178953471159903352227492495111071
x2 = 6466809655659049447127736275529851894657569985804963410176865782113074947167
p = 68101195620
I don't know whether there are other p'.

l = 12
x1 = 132503538560485423319724633262218262792296147003813662398252348727558616998821387759658729802732555377599590456096450977511271450086857949046098328487779612488702544062780731169071526325427862701033062986918854245283037892816922645703778218888876645148150396130125974518827547039720412359298502758101864465267219269598121846675000819173555118275197412936184329860639224312426860362491131729109976241526141192634523046343361089218776687819810873911761177080056675776644326080790638190845283447304699879671516831798277084926941086929776037986892223389603958335825223
x2 = 131643270083452525545713630444392174853686642378302602432151533578354175874660202842105881983788182087244225335788180044756143002547651778418104898394856368040582966040636443591550863800820890232349510212502022967044635049530630094703200089437589000344385691841539471759564428710508659169951391360884974854486267690231936418935298696990496810984630182864946252125857984234200409883080311780173125332191068011865349489020080749633049912518609380810021976861585063983190710264511339441915235691015858985314705640801109163008926275586193293353829677264797719957439635
p = 12920503469397123671484716106535636962543473
I don't know whether there are other p'.

l = 12
x1 = 202682323504122627687421150801262260096036559509855209647629958481910539332845439801686105377638207777951377858833355315514789392768449139095245989465034831121409966815913228535487871119596033570221780568122582453813989896850354963963579404589216380209702064994881800638095974725735826187029705991851861437712496046570494304535548139347915753682466465910703584162857986211423274841044480134909827293577782500978784365107166584993093904666548341384683749686200216537120741867400554787359905811760833689989323176213658734291045194879271258061845641982134589988950037
x2 = 181061672413088057213056735163589264228345385049856782741314216892873615377401934633944987733964053303318802550909800629914413353049208324641813340834741135897326747139541660984388998099026320957569795775586586220775707569049815466134899066365036389427046307790466751981020951925232623622327618223732816807936229082125018442471614910956092251885124883253591153056364654734271407552319665257904066307163047533658914884519547950787163679609742158608089946055315496165960274610016198230291033540306847172592039765417365770579502834927831791804602945514484791644440788
p = 21705376375228755718179424140760701489963164

Mencetak gol

Seperti disebutkan di atas, skor program Anda adalah yang tertinggi lyang diselesaikan oleh program dalam waktu kurang dari 1 menit. Lebih khusus lagi, program Anda akan dijalankan pada 5 kejadian acak dengan itu l, dan itu harus menghasilkan jawaban yang benar pada semua 5, dengan waktu rata-rata di bawah 1 menit. Skor suatu program akan menjadi yang tertinggi lyang berhasil. Tiebreaker akan menjadi waktu rata-rata untuk itu l.

Untuk memberi Anda gambaran tentang skor apa yang ingin Anda tuju, saya menulis sebuah solusi brute-force yang sangat sederhana. Itu mendapat skor 5. Saya menulis pemecah jauh lebih mewah. Itu mendapat skor 12 atau 13, tergantung keberuntungan.


Detail

Untuk komparabilitas sempurna di seluruh jawaban, saya akan mengatur waktu pengiriman pada laptop saya untuk memberikan skor kanonik. Saya juga akan menjalankan contoh yang dipilih secara acak yang sama pada semua pengiriman, untuk mengurangi keberuntungan. Laptop saya memiliki 4 CPU, CPU i5-4300U @ 1.9 GHz, RAM 7.5G.

Jangan ragu untuk memposting skor sementara berdasarkan waktu Anda sendiri, cukup jelaskan apakah itu sementara atau kanonik.


Semoga program tercepat menang!

isaacg
sumber
Apakah aproksimasi harus yang terdekat?
Yytsi
@ TuukkaX Setiap l^2nomor bit yang l-bits jauh dari menjadi faktor kedua angka berfungsi. Namun biasanya hanya ada satu.
isaacg
Berikut ini disertasi dengan beberapa gagasan algoritma: tigerprints.clemson.edu/cgi/… . Terutama bagus dan sederhana adalah yang ada di bagian 5.1.1
isaacg
The i5-4300U memiliki 2 core (4 thread), tidak 4 core.
Anders Kaseorg

Jawaban:

3

Rust, L = 13

src/main.rs

extern crate gmp;
extern crate rayon;

use gmp::mpz::Mpz;
use gmp::rand::RandState;
use rayon::prelude::*;
use std::cmp::max;
use std::env;
use std::mem::swap;

// Solver

fn solve(x1: &Mpz, x2: &Mpz, l: usize) -> Option<Mpz> {
    let m = l*l - 1;
    let r = -1i64 << l-2 .. 1 << l-2;
    let (mut x1, mut x2) = (x1 - (3 << l-2), x2 - (3 << l-2));
    let (mut a1, mut a2, mut b1, mut b2) = (Mpz::one(), Mpz::zero(), Mpz::zero(), Mpz::one());
    while !x2.is_zero() &&
        &(max(a1.abs(), b1.abs()) << l-2) < &x1 &&
        &(max(a2.abs(), b2.abs()) << l-2) < &x2
    {
        let q = &x1/&x2;
        swap(&mut x1, &mut x2);
        x2 -= &q*&x1;
        swap(&mut a1, &mut a2);
        a2 -= &q*&a1;
        swap(&mut b1, &mut b2);
        b2 -= &q*&b1;
    }
    r.clone().into_par_iter().map(|u| {
        let (mut y1, mut y2) = (&x1 - &a1*u + (&b1 << l-2), &x2 - &a2*u + (&b2 << l-2));
        for _ in r.clone() {
            let d = Mpz::gcd(&y1, &y2);
            if d.bit_length() >= m {
                let d = d.abs();
                let (mut k, k1) = (&d >> l*l, &d >> l*l-1);
                while k < k1 {
                    k += 1;
                    if (&d%&k).is_zero() {
                        return Some(&d/&k)
                    }
                }
            }
            y1 -= &b1;
            y2 -= &b2;
        }
        None
    }).find_any(|o| o.is_some()).unwrap_or(None)
}

// Test harness

fn randbits(rnd: &mut RandState, bits: usize) -> Mpz {
    rnd.urandom(&(Mpz::one() << bits - 1)) + (Mpz::one() << bits - 1)
}

fn gen(rnd: &mut RandState, l: usize) -> (Mpz, Mpz, Mpz) {
    let p = randbits(rnd, l*l);
    (randbits(rnd, l*l*l)*&p + randbits(rnd, l),
     randbits(rnd, l*l*l)*&p + randbits(rnd, l),
     p)
}

fn is_correct(x1: &Mpz, x2: &Mpz, l: usize, p_prime: &Mpz) -> bool {
    p_prime.bit_length() == l*l &&
        (x1 % p_prime).bit_length() == l &&
        (x2 % p_prime).bit_length() == l
}

fn random_test(l: usize, n: usize) {
    let mut rnd = RandState::new();  // deterministic seed
    for _ in 0..n {
        let (x1, x2, p) = gen(&mut rnd, l);
        println!("x1 = {}", x1);
        println!("x2 = {}", x2);
        println!("l = {}", l);
        println!("p = {}", p);
        println!("x1 % p = {}", &x1 % &p);
        println!("x2 % p = {}", &x2 % &p);
        assert!(is_correct(&x1, &x2, l, &p));
        let p_prime = solve(&x1, &x2, l).expect("no solution found!");
        println!("p' = {}", p_prime);
        assert!(is_correct(&x1, &x2, l, &p_prime));
        println!("correct");
    }
}

// Command line interface

fn main() {
    let args = &env::args().collect::<Vec<_>>();
    if args.len() == 4 && args[1] == "--random" {
        if let (Ok(l), Ok(n)) = (args[2].parse(), args[3].parse()) {
            return random_test(l, n);
        }
    }
    if args.len() == 4 {
        if let (Ok(x1), Ok(x2), Ok(l)) = (args[1].parse(), args[2].parse(), args[3].parse()) {
            match solve(&x1, &x2, l) {
                None => println!("no solution found"),
                Some(p_prime) => println!("{}", p_prime),
            }
            return;
        }
    }
    println!("Usage:");
    println!("    {} --random L N", args[0]);
    println!("    {} X1 X2 L", args[0]);
}

Cargo.toml

[package]
name = "agcd"
version = "0.1.0"
authors = ["Anders Kaseorg <[email protected]>"]

[dependencies]
rayon = "0.7.1"
rust-gmp = "0.5.0"

Untuk berlari

cargo build --release
target/release/agcd 56007668599 30611458895 3
target/release/agcd --random 13 5
Anders Kaseorg
sumber
Hasil resmi adalah l = 13, dengan waktu rata-rata 41,53d. l = 14 mengambil sedikit lebih dari 2m rata-rata.
isaacg
2

Mathematica, L = 5

di sini adalah 5 solusi cepat

(l = #3;
a = #1 - Range[2^(l - 1), 2^l];
b = #2 - Range[2^(l - 1), 2^l];
Last@Intersection[
Flatten[Table[Select[Divisors@a[[i]], # < 2^l^2 &], {i, Length@a}],
 1],
Flatten[Table[Select[Divisors@b[[i]], # < 2^l^2 &], {i, Length@b}],
 1]]
) &

Input
[x1, x2, L]

agar siapa pun dapat menguji ini, ini adalah generator kunci

l = 5;
a = RandomInteger[{2^(l^2 - 1), 2^(l^2)}]
b = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
c = RandomInteger[{2^(l - 1), 2^l - 1}];
f = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
g = RandomInteger[{2^(l - 1), 2^l - 1}];
x = a*b + c
y = a*f + g

pilih menjalankan nilai L, kode dan Anda akan mendapatkan tiga angka.
tempatkan dua yang terakhir bersama dengan L sebagai input, dan Anda harus mendapatkan yang pertama

J42161217
sumber
Saya telah memverifikasi solusi ini karena mendapat skor l = 5. Saya akan mengatur waktu jika waktu diperlukan sebagai tiebreak.
isaacg
1

Mathematica, L = 12

ClearAll[l, a, b, k, m];
(l = #3;
a = #1 - Range[2^(l - 1), 2^l];
b = #2 - Range[2^(l - 1), 2^l];
k = Length@a;
m = Length@b;
For[i = 1, i <= k, i++, 
For[j = 1, j <= m, j++, If[2^(l^2 - 1) < GCD[a[[i]], b[[j]]],
 If[GCD[a[[i]], b[[j]]] > 2^l^2, 
  Print@Max@
    Select[Divisors[GCD[a[[i]], b[[j]]]], 
     2^(l^2 - 1) < # < 2^l^2 &]; Abort[], 
  Print[GCD[a[[i]], b[[j]]]];
  Abort[]]]]]) &

masukan [x1, x2, L]

agar siapa pun dapat menguji ini, ini adalah generator kunci

l = 12;
a = RandomInteger[{2^(l^2 - 1), 2^(l^2)}]
b = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
c = RandomInteger[{2^(l - 1), 2^l - 1}];
f = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
g = RandomInteger[{2^(l - 1), 2^l - 1}];
x = a*b + c
y = a*f + g

pilih nilai L, jalankan kodenya dan Anda akan mendapatkan tiga angka.
tempatkan dua yang terakhir bersama dengan L sebagai input, dan Anda harus mendapatkan yang pertama

J42161217
sumber
Ketika saya menguji ini dengan l = 12, itu memberikan hasil yang salah. Secara khusus, pembagi yang dihasilkan adalah angka 146-bit, yang terlalu besar. Saya pikir Anda hanya perlu sedikit perubahan untuk memperbaikinya.
isaacg
Saya menambahkan kasus gagal sebagai contoh terakhir di atas. Solver Anda memberikan jawaban sama dengan 3 * p, yang agak terlalu besar. Melihat kode Anda, sepertinya Anda hanya memeriksa bahwa output Anda memiliki setidaknya l^2bit, tetapi Anda juga perlu memeriksa bahwa ia memiliki paling banyak l^2bit.
isaacg
Pada test case yang sama yang gagal sebelumnya, itu masih tidak berfungsi. Saya tidak super akrab dengan Mathematica, tapi itu tampak seperti keluar tanpa output. Saya pikir masalahnya adalah menemukan faktor yang terlalu besar, dan alih-alih menemukan pembagi faktor dalam rentang yang diinginkan, itu hanya melewatinya.
isaacg
Skor resmi adalah L = 12, dengan waktu rata-rata 52.7s. Sudah selesai dilakukan dengan baik!
isaacg
1

Python, L = 15, waktu rata-rata 39.9s

from sys import argv
from random import seed, randrange

from gmpy2 import gcd, mpz
import gmpy2

def mult_buckets(buckets):
    if len(buckets) == 1:
        return buckets[0]
    new_buckets = []
    for i in range(0, len(buckets), 2):
        if i == len(buckets) - 1:
            new_buckets.append(buckets[i])
        else:
            new_buckets.append(buckets[i] * buckets[i+1])
    return mult_buckets(new_buckets)


def get_products(xs, l):
    num_buckets = 1000
    lower_r = 1 << l - 1
    upper_r = 1 << l
    products = []
    bucket_size = (upper_r - lower_r)//num_buckets + 1
    for x in xs:
        buckets = []
        for bucket_num in range(num_buckets):
            product = mpz(1)
            for r in range(lower_r + bucket_num * bucket_size,
                           min(upper_r, lower_r + (bucket_num + 1) * bucket_size)):
                product *= x - mpz(r)
            buckets.append(product)
        products.append(mult_buckets(buckets))
    return products

def solve(xs, l):
    lower_r = 2**(l - 1)
    upper_r = 2**l
    lower_p = 2**(l**2 - 1)
    upper_p = 2**(l**2)

    products = get_products(xs, l)
    overlap = gcd(*products)
    candidate_lists = []
    for x in xs:
        candidates = []
        candidate_lists.append(candidates)
        for r in range(lower_r, upper_r):
            common_divisor = gcd(x-r, overlap)
            if common_divisor >= lower_p:
                candidates.append(common_divisor)
    to_reduce = []
    for l_candidate in candidate_lists[0]:
        for r_candidate in candidate_lists[1]:
            best_divisor = gcd(l_candidate, r_candidate)
            if lower_p <= best_divisor < upper_p:
                return best_divisor
            elif best_divisor > upper_p:
                to_reduce.append(best_divisor)
    for divisor in to_reduce:
        cutoff = divisor // lower_p
        non_divisors = []
        for sub_divisor in range(2, cutoff + 1):
            if any(sub_divisor % non_divisor == 0 for non_divisor in non_divisors):
                continue
            quotient, remainder = gmpy2.c_divmod(divisor, sub_divisor)
            if remainder == 0 and lower_p <= quotient < upper_p:
                return quotient
            if quotient < lower_p:
                break
            if remainder != 0:
                non_divisors.append(sub_divisor)

def is_correct(x1, x2, l, p_prime):
    p_prime_is_good = p_prime >> (l**2 - 1) and not p_prime >> l ** 2
    x1_is_good = (x1 % p_prime) >> (l-1) and not (x1 % p_prime) >> l
    x2_is_good = (x2 % p_prime) >> (l-1) and not (x2 % p_prime) >> l
    return bool(p_prime_is_good and x1_is_good and x2_is_good)

if __name__ == '__main__':
    seed(0)

    l = int(argv[1])
    reps = int(argv[2])

    def randbits(bits):
        return randrange(2 ** (bits - 1), 2 ** bits)

    for _ in range(reps):
        p = randbits(l ** 2)
        print("p = ", p)
        xs = []
        for i in range(2):
            q_i = randbits(l ** 3)
            print("q", i, "=", q_i)
            r_i = randbits(l)
            print("r", i, "=", r_i)
            x_i = q_i * p + r_i
            print("x", i, "=", x_i)
            xs.append(x_i)

        res = solve(xs, l)
        print("answer = ", res)
        print("Correct?", is_correct(xs[0], xs[1], l, res))

Kode ini didasarkan pada fakta bahwa produk x1 - r untuk semua nilai yang mungkin dari r harus kelipatan p, dan produk dari x2 - r juga harus. Sebagian besar waktu dihabiskan untuk mengambil gcd dari dua produk, yang masing-masing memiliki sekitar 60.000.000 bit. Gcd, yang hanya memiliki sekitar 250.000 bit, kemudian dibandingkan dengan masing-masing nilai xr sekali lagi, untuk mengekstrak kandidat p '. Jika mereka agak terlalu besar, pembagian percobaan digunakan untuk menguranginya ke kisaran yang sesuai.

Ini didasarkan pada pustaka gmpy2 untuk Python, yang merupakan pembungkus tipis untuk pustaka GNU MP, yang secara khusus memiliki rutin gcd yang sangat bagus.

Kode ini adalah utas tunggal.

isaacg
sumber