ตัวหารสามัญโดยประมาณที่เร็วที่สุด


13

ภาพรวม

ในการท้าทายนี้คุณจะได้รับตัวเลขสองตัวซึ่งทั้งคู่มีค่าออฟเซ็ตขนาดเล็กมากกว่าจำนวนของขนาดกลางจำนวนมาก คุณต้องส่งออกหมายเลขขนาดกลางที่เกือบจะเป็นตัวหารของตัวเลขทั้งสองยกเว้นการชดเชยขนาดเล็ก

ขนาดของตัวเลขที่เกี่ยวข้องจะถูกทำให้เป็นพารามิเตอร์โดยพารามิเตอร์ความยากลำบาก, l. วัตถุประสงค์ของคุณคือการแก้ปัญหาให้ได้มากที่สุดlในเวลาไม่ถึง 1 นาที


ติดตั้ง

ในปัญหาที่กำหนดจะมีหมายเลขลับpซึ่งจะเป็นหมายเลขบิตแบบสุ่มl^2( l*l) จะมีตัวคูณสองตัวq1, q2ซึ่งจะเป็นl^3ตัวเลขสุ่มและจะมีสองออฟเซ็r1, r2ตซึ่งจะเป็นlตัวเลขบิตสุ่ม

ข้อมูลเข้าสู่โปรแกรมของคุณจะถูกx1, x2กำหนดเป็น:

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

นี่คือโปรแกรมสำหรับสร้างกรณีทดสอบใน 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)

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


โปรแกรมของคุณ

ป.ร. ให้ไว้x1, x2และlคุณจะต้องพบl^2จำนวนบิตp'เช่นนั้นx1 % p'และx2 % p'มีทั้งlตัวเลขบิต pจะได้ผลเสมอแม้ว่าอาจมีความเป็นไปได้อื่น ๆ นี่คือฟังก์ชั่นเพื่อตรวจสอบการแก้ไข:

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)

ตัวอย่าง

สมมติว่าlเป็น 3. โปรแกรมกำเนิดหยิบจำนวน 9 บิตซึ่งในกรณีนี้คือp 442เครื่องกำเนิดไฟฟ้าหยิบสอง3หมายเลขบิตซึ่งเป็นr1, r2 4, 7เครื่องกำเนิดไฟฟ้าหยิบสอง27หมายเลขบิตซึ่งเป็นq1, q2 117964803, 101808039เพราะการเลือกเหล่านี้มีx1, x252140442930, 44999153245

โปรแกรมของคุณจะได้รับ52140442930, 44999153245เป็นอินพุตและต้องส่งออกตัวเลข 9 บิต (ในช่วง[256, 511]) เช่นนั้น52140442930และ44999153245โมดูโลหมายเลขนั้นให้ตัวเลข 3 บิต (ในช่วง[4, 7]) เป็นค่าเท่านั้นเช่นในกรณีนี้เพื่อให้โปรแกรมของคุณจะต้องมีการส่งออก442442


ตัวอย่างเพิ่มเติม

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

เกณฑ์การให้คะแนน

ดังที่กล่าวไว้ข้างต้นคะแนนโปรแกรมของคุณสูงที่สุดlที่โปรแกรมดำเนินการให้เสร็จภายใน 1 นาที โดยเฉพาะอย่างยิ่งโปรแกรมของคุณจะทำงานกับอินสแตนซ์สุ่ม 5 รายการโดยlจะต้องส่งออกคำตอบที่ถูกต้องสำหรับทั้ง 5 ด้วยเวลาเฉลี่ยต่ำกว่า 1 นาที คะแนนของโปรแกรมจะสูงที่สุดlที่จะประสบความสำเร็จ Tiebreaker จะเป็นเวลาเฉลี่ยในสิ่งlนั้น

เพื่อให้คุณทราบถึงสิ่งที่จะทำคะแนนได้ฉันเขียนตัวแก้กำลังแบบเดรัจฉานที่ง่ายมาก มันได้คะแนน 5 ฉันเขียนนักคิดที่ยอดเยี่ยมกว่านี้ มันได้คะแนน 12 หรือ 13 ขึ้นอยู่กับโชค


รายละเอียด

สำหรับการเปรียบเทียบคำตอบที่สมบูรณ์แบบฉันจะใช้เวลาส่งแล็ปท็อปของฉันเพื่อให้คะแนนตามมาตรฐาน ฉันจะใช้อินสแตนซ์ที่สุ่มเลือกเดียวกันในทุกการส่งเพื่อบรรเทาโชคบ้าง แล็ปท็อปของฉันมี 4 CPU, i5-4300U CPU @ 1.9 GHz, RAM 7.5G

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


โปรแกรมที่เร็วที่สุดอาจชนะ!


การประมาณต้องใกล้เคียงที่สุดหรือไม่
Yytsi

@ TuukkaX l^2หมายเลขบิตใด ๆที่ - lอยู่ห่างจากการเป็นปัจจัยของตัวเลขทั้งสองทำงาน อย่างไรก็ตามโดยทั่วไปจะมีเพียงหนึ่งเดียว
isaacg

นี่เป็นวิทยานิพนธ์ที่มีความคิดขั้นตอนวิธีการบางอย่างtigerprints.clemson.edu/cgi/... ดีและเรียบง่ายโดยเฉพาะอย่างยิ่งเป็นหนึ่งในส่วน 5.1.1
isaacg

i5-4300Uมี 2 แกน (4 หัวข้อ) ไม่ใช่ 4 แกน
Anders Kaseorg

คำตอบ:


3

สนิม, 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 <andersk@mit.edu>"]

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

วิ่ง

cargo build --release
target/release/agcd 56007668599 30611458895 3
target/release/agcd --random 13 5

ผลการแข่งขันอย่างเป็นทางการคือ l = 13 โดยมีเวลาเฉลี่ย 41.53 วินาที l = 14 ใช้เวลาเฉลี่ยมากกว่า 2m เล็กน้อย
isaacg

2

Mathematica, L = 5

นี่คือทางออกที่รวดเร็ว 5

(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]]
) &

อินพุต
[x1, x2, L]

เพื่อให้ทุกคนสามารถทดสอบได้นี่คือตัวสร้างหลัก

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

เลือกรันค่า L รหัสและคุณจะได้รับตัวเลขสามตัว
วางสองอันสุดท้ายพร้อมกับ L เป็นอินพุทและคุณควรหาอันแรก


ฉันได้ตรวจสอบวิธีการแก้ปัญหานี้ว่าได้รับคะแนน l = 5 ฉันจะใช้เวลาถ้าจำเป็นต้องใช้เวลาเป็น tiebreaker
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[]]]]]) &

อินพุต [x1, x2, L]

เพื่อให้ทุกคนสามารถทดสอบได้นี่คือตัวสร้างหลัก

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

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


เมื่อฉันทดสอบสิ่งนี้ด้วยl = 12จะให้ผลลัพธ์ที่ไม่ถูกต้อง ตัวหารผลลัพธ์คือหมายเลข 146 บิตซึ่งมีขนาดใหญ่เกินไป ฉันคิดว่าคุณจะต้องมีการเปลี่ยนแปลงเล็กน้อยเพื่อแก้ไขปัญหานี้
isaacg

ฉันเพิ่มกรณีที่ล้มเหลวเป็นตัวอย่างสุดท้ายข้างต้น นักแก้ปัญหาของคุณให้คำตอบเท่ากับ 3 * p ซึ่งค่อนข้างใหญ่ไปหน่อย ดูที่รหัสของคุณดูเหมือนว่าคุณจะตรวจสอบว่าเอาต์พุตของคุณมีl^2บิตอย่างน้อยแต่คุณต้องตรวจสอบว่ามีl^2บิตมากที่สุด
isaacg

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

ขอให้เรายังคงอภิปรายนี้ในการแชท
isaacg

คะแนนทางการคือ L = 12 โดยมีเวลาเฉลี่ย 52.7 วินาที ทำได้ดี!
isaacg

1

Python, L = 15, เวลาเฉลี่ย 39.9 วินาที

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))

รหัสนี้ขึ้นอยู่กับความจริงที่ว่าผลิตภัณฑ์ของ x1 - r สำหรับค่าที่เป็นไปได้ทั้งหมดของ r จะต้องเป็นหลายเท่าของ p และผลิตภัณฑ์ของ x2 - r ต้องเป็นอย่างดี เวลาส่วนใหญ่ใช้เวลากับ gcd ของสองผลิตภัณฑ์ซึ่งแต่ละอันมีประมาณ 60,000,000 บิต gcd ซึ่งมีประมาณ 250,000 บิตเท่านั้นจะถูกเปรียบเทียบกับค่า xr แต่ละค่าอีกครั้งเพื่อแยกตัวเลือกของ p หากมีขนาดใหญ่เกินไปการแบ่งรุ่นทดลองใช้จะลดขนาดลงในช่วงที่เหมาะสม

นี่คือพื้นฐานของไลบรารี gmpy2 สำหรับ Python ซึ่งเป็น wrapper ขนาดเล็กสำหรับไลบรารี GNU MP ซึ่งโดยเฉพาะอย่างยิ่งมีรูทีน gcd ที่ดีจริงๆ

รหัสนี้เป็นเธรดเดียว

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