가장 빠른 근사 제수


13

개요

이 과제에서는 중간 크기의 배수보다 작은 작은 오프셋 인 두 개의 숫자가 주어집니다. 작은 오프셋을 제외하고 두 숫자의 약수 인 중간 크기 숫자를 출력해야합니다.

관련된 숫자의 크기는 난이도 매개 변수로 매개 변수화됩니다 l. 귀하의 목표는 l1 분 안에 최대한 큰 문제를 해결하는 것입니다 .


설정

주어진 문제에서, 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)

l3 이라고 가정 하십시오. 생성기 프로그램은 9 비트 숫자를 선택합니다 ( p이 경우에는) 442. 발전기는 두 개의 픽업 3에 대한 비트 숫자 r1, r2이다 4, 7. 발전기는 두 개의 픽업 27에 대한 비트 숫자 q1, q2이다 117964803, 101808039. 때문에 이러한 선택의, x1, x2있습니다 52140442930, 44999153245.

프로그램들 될 52140442930, 44999153245입력 및 출력해야 (범위의 9 비트 수가 [256, 511]되도록) 5214044293044999153245(범위주고 그 번호 3 비트 값을 모듈로 (modulo) [4, 7]). 442이 경우 유일한 값이므로 프로그램에서 출력해야합니다 442.


더 많은 예

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

채점

위에서 언급했듯이 프로그램 점수는 l1 분 이내에 완료되는 최고 점수입니다 . 보다 구체적으로 프로그램은 5 개의 임의 인스턴스에서 실행되며 5 l분마다 평균 시간이 1 분 미만인 정답을 출력해야합니다. 프로그램의 점수는 l성공한 최고 점수입니다 . Tiebreaker는 평균 시간입니다 l.

어떤 점수를 목표로하는지에 대한 아이디어를주기 위해 매우 간단한 무차별 솔버를 작성했습니다. 5 점을 받았습니다. 나는 훨씬 더 멋진 솔버를 썼습니다. 운에 따라 12 점 또는 13 점을 받았습니다.


세부

답을 완벽하게 비교하기 위해 랩톱에 제출하여 정식 점수를 제공합니다. 또한 행운을 다소 완화하기 위해 모든 제출에서 무작위로 선택된 동일한 인스턴스를 실행합니다. 내 노트북에는 4 개의 CPU, 1.9GHz에서 i5-4300U CPU, 7.5G의 RAM이 있습니다.

자신의 타이밍에 따라 임시 점수를 게시하고 임시인지 정식인지 명확하게 알려주십시오.


가장 빠른 프로그램이 이길 수 있습니다!


근사값이 가장 근접해야합니까?
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 2016 년

2

수학, 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임을 확인했습니다. 타이 브레이커로 타이밍이 필요한 경우 시간을 정할 것입니다.
isaacg

1

수학, 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 2016 년


공식 점수는 L = 12이며 평균 시간은 52.7 초입니다. 잘 했어!
isaacg 2016 년

1

파이썬, L = 15, 평균 시간 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))

이 코드는 가능한 모든 r 값에 대한 x1-r의 곱은 p의 배수 여야하고 x2-r의 곱도 같아야한다는 사실을 기반으로합니다. 대부분의 시간은 두 제품의 gcd를 가져 오는 데 소비되며 각 제품에는 약 60,000,000 비트가 있습니다. 약 250,000 비트만을 갖는 gcd는 p '후보를 추출하기 위해 각각의 xr 값과 다시 한번 비교된다. 그것들이 너무 크다면, 시험 분할은 그것들을 적절한 범위로 줄이기 위해 사용됩니다.

이것은 파이썬 용 gmpy2 라이브러리를 기반으로합니다.이 라이브러리는 GNU MP 라이브러리의 얇은 래퍼이며 특히 gcd 루틴이 우수합니다.

이 코드는 단일 스레드입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.