일부 비트를 삭제하고 계산


26

2^n길이가 다른 모든 이진 문자열을 고려 n하고 가정하십시오 n > 2. b < n/2각 이진 문자열에서 정확히 비트 를 삭제 하고 길이가 긴 문자열을 남겨 둘 수 있습니다 n-b. 남아있는 고유 문자열의 수는 삭제하는 비트에 따라 다릅니다. 목표가 가능한 한 적은 수의 남은 다른 문자열을 남겨 두는 것으로 가정하면,이 과제는의 함수로 몇 개를 남길 수 있는지 계산하는 코드를 작성하는 것입니다 n.

n=3b = 1. 당신은 두 개의 문자열을 남길 수 있습니다 1100.

의 경우 n=9b = 1,2,3,4우리는이70,18,6,2

의 경우 n=8b = 1,2,3우리는이40,10,4

의 경우 n=7b = 1,2,3우리는이20,6,2

의 경우 n=6b = 1,2우리는이12,4

의 경우 n=5b = 1,2우리는이6,2

이 질문은 원래 2014 년에 MO 와 다른 형식으로 제기되었습니다 .

입력과 출력

코드는 정수를 가져 와서 시작 하고 증가하는 n각 값에 대해 단일 정수를 출력 해야합니다 .bb = 0

점수

귀하의 점수는 Linux 기반 PC에서 1 분 안에 n코드가 완료되는 가장 큰 점수입니다 b < n/2. 타이 브레이크의 경우 b, 공동 최대 n승리 를 위해 코드가 얻는 최대 값 입니다. 너무 그 기준의 최대 값에 대한 가장 빠른 코드에 타이 브레이크의 경우 nb결정한다. 시간이 서로 1-2 초 내에 있으면 첫 번째로 게시 된 답변이 우선합니다.

언어와 라이브러리

원하는 라이브러리 언어를 사용할 수 있습니다. 코드를 실행해야하기 때문에 무료로 (맥주 에서처럼) Linux에서 작동하면 도움이 될 것입니다.


b > 0추가 입력 요구 사항을 가정 하고 있습니까? 또는 것 n=3b=0간단하게 출력 2^n결과?
Kevin Cruijssen

@KevinCruijssen 2^n실제로 출력되어야합니다 .
Anush

또한 입력은 single n및 single b이지만 점수는 1 분 안에 n코드가 완료되는 최대 점수입니다 b < n/2. 이 경우 단일 입력을 가지고 n모든 결과를 출력 하는 것이 낫지 0 <= b < n/2않습니까? 또는 두 개의 프로그램 / 기능을 제공해야합니다. 하나는 두 개의 입력 nb받고 다른 하나는 입력 만 n받고 범위 내 모든 결과를 출력 0 <= b < n/2합니까?
Kevin Cruijssen

2
글쎄, 난 당신의 도전을 이미 상향 조정 했으므로 다시는 할 수 없습니다. :) 나는 이것을 효율적으로 계산하는 방법을 모릅니다 (효율적인 O 알고리즘은 내가 항상 나쁜 것입니다. 매우 흥미로운 도전입니다. 사람들이 어떤 대답을하는지 궁금합니다.
케빈 크루이 센

2
실제 사례가 있습니까? 정확성과 속도를 비교할 때 시작하기에 좋은 장소입니다.
maxb

답변:


6

파이썬 2.7 / 구로 비 n = 9

이 솔루션은 부울 혼합 정수 문제 (MIP)에 대한 Gurobi의 ILP 솔버를 매우 간단하게 사용합니다.

유일한 트릭은 문제 크기를 반으로 줄이기 위해 1의 보수로 대칭을 취하는 것입니다.

Gurobi LLC의 제한된 시간 "무료"라이센스를 사용하면 2000 제약 조건으로 제한되지만 10 del 1을 해결하는 것은 내 노트북의 60 초 제한 시간을 훨씬 초과합니다.

from gurobipy import *
from itertools import combinations

def mincover(n,d):
    bs = pow(2,n-1-d)
    m = Model()
    m.Params.outputFlag = 0
    b = {}
    for i in range(bs):
      b[i] = m.addVar(vtype=GRB.BINARY, name="b%d" % i)
    m.update()
    for row in range(pow(2,n-1)):
      x = {}
      for i in combinations(range(n), n-d):
        v = 0
        for j in range(n-d):
          if row & pow(2,i[j]):
            v += pow(2,j)
        if v >= bs:
          v = 2*bs-1-v
        x[v] = 1
      m.addConstr(quicksum(b[i] for i in x.keys()) >= 1)
    m.setObjective(quicksum(b[i] for i in range(bs) ), GRB.MINIMIZE)
    m.optimize()
    return int(round(2*m.objVal,0))

for n in range(4,10):
    for d in range((n//2)+1):
        print n, d, mincover(n,d)

업데이트 + CORR : 10,2는 최적의 솔루션 크기가 31입니다 (예 : Gurobi는 크기가 30 인 대칭 솔루션이 없음을 나타냅니다 (문제는 불가능합니다). 정수 패턴 0 7 13 14 25 28 35 36 49 56 63 64 95 106 118 128 147 159 170 182 195 196 200 207 225 231 240 243 249 252 255또는0 7 13 14 19 25 28 35 36 49 56 63 64 95 106 118 128 159 170 182 195 196 200 207 225 231 240 243 249 252 255


"가장 빠른 청구 된 무한 현상금"기록을 위반 했습니까?
user202729

여기에 현상금이 표시되지 않습니다. 무슨 뜻입니까?
jayprich

@ user202729 예. 너무 낮게 설정했습니다. n = 10으로 설정해야합니다. :)
Anush

실제로 n = 9에서 해결하는 것은 쉬운 일이 아닙니다. 그렇기 때문에 OP는 기존 라이브러리를 사용합니다 (내 것과 같은 손으로 작성한 솔루션보다 낫습니다).
user202729

1
감사합니다 @ChristianSievers MO는 10,2가 반박하거나 확인할 수없는 비대칭 최적화 만 가지고 있다고 주장합니다. 최대 n = 9까지 작동하는 대칭 가정 바로 가기를 제거하면 Gurobi가 필요한 시간에 여전히 n = 9까지 해결할 수 있습니다.
jayprich

3

C ++, n = 6

작은 최적화로 무차별 대입.

#include<cassert>
#include<iostream>
#include<vector>

// ===========
/** Helper struct to print binary representation.
`std::cout<<bin(str,len)` prints (str:len) == the bitstring 
represented by last (len) bits of (str).
*/
struct bin{
    int str,len;
    bin(int str,int len):str(str),len(len){}
};
std::ostream& operator<<(std::ostream& str,bin a){
    if(a.len)
        return str<<bin(a.str>>1,a.len-1)<<char('0'+(a.str&1));
    else if(a.str)
        return str<<"...";
    else
        return str;
}
// ===========

/// A patten of (len) bits of ones.
int constexpr pat1(int len){
    return (1<<len)-1;
}

// TODO benchmark: make (res) global variable?

/**Append all distinct (subseqs+(sfx:sfxlen)) of (str:len) 
with length (sublen) to (res).
*/
void subseqs_(
    int str,int len,int sublen,
    int sfx,int sfxlen,
    std::vector<int>& res
){
    // std::cout<<"subseqs_ : str = "<<bin(str,len)<<", "
    // "sublen = "<<sublen<<", sfx = "<<bin(sfx,sfxlen)<<'\n';

    assert(len>=0);

    if(sublen==0){ // todo remove some branches can improve perf?
        res.push_back(sfx);
        return;
    }else if(sublen==len){
        res.push_back(str<<sfxlen|sfx);
        return;
    }else if(sublen>len){
        return;
    }

    if(str==0){
        res.push_back(sfx);
        return;
    }

    int nTrail0=0;
    for(int ncut;str&&nTrail0<sublen;

        ++nTrail0,
        ncut=__builtin_ctz(~str)+1, // cut away a bit'0' of str
        // plus some '1' bits
        str>>=ncut,
        len-=ncut
    ){
        ncut=__builtin_ctz(str)+1; // cut away a bit'1' of str
        subseqs_(str>>ncut,len-ncut,sublen-nTrail0-1,
            sfx|1<<(sfxlen+nTrail0),sfxlen+nTrail0+1,
            res
        ); // (sublen+sfxlen) is const. TODO global var?
    }

    if(nTrail0+len>=sublen) // this cannot happen if len<0
        res.push_back(sfx);
}

std::vector<int> subseqs(int str,int len,int sublen){
    assert(sublen<=len);
    std::vector<int> res;
    if(__builtin_popcount(str)*2>len){ // too many '1's, flip [todo benchmark]
        subseqs_(pat1(len)^str,len,sublen,0,0,res);
        int const p1sublen=pat1(sublen);
        for(int& r:res)r^=p1sublen;
    }else{
        subseqs_(str,len,sublen,0,0,res);
    }
    return res;
}

// ==========

/** Append all distinct (supersequences+(sfx:sfxlen)) of (str:len)
with length (suplen) to (res).
Define (a) to be a "supersequence" of (b) iff (b) is a subsequence of (a).
*/
void supseqs_(
    int str,int len,int suplen,
    int sfx,int sfxlen,
    std::vector<int>& res
){
    assert(suplen>=len);

    if(suplen==0){
        res.push_back(sfx);
        return;
    }else if(suplen==len){
        res.push_back(str<<sfxlen|sfx);
        return;
    }

    int nTrail0; // of (str)
    if(str==0){
        res.push_back(sfx);
        // it's possible that the supersequence is '0000..00'
        nTrail0=len;
    }else{
        // str != 0 -> str contains a '1' bit ->
        // supersequence cannot be '0000..00'
        nTrail0=__builtin_ctz(str);
    }
    // todo try `nTrail0=__builtin_ctz(str|1<<len)`, eliminates a branch
    // and conditional statement

    for(int nsupTrail0=0;nsupTrail0<nTrail0;++nsupTrail0){
        // (nsupTrail0+1) last bits of supersequence matches with 
        // nsupTrail0 last bits of str.
        supseqs_(str>>nsupTrail0,len-nsupTrail0,suplen-1-nsupTrail0,
            sfx|1<<(nsupTrail0+sfxlen),sfxlen+nsupTrail0+1,
            res);
    }

    int const strMatch=str?nTrail0+1:len; 
    // either '1000..00' or (in case str is '0000..00') the whole (str)

    for(int nsupTrail0=suplen+strMatch-len;nsupTrail0-->nTrail0;){
        // because (len-strMatch)<=(suplen-1-nsupTrail0),
        // (nsupTrail0<suplen+strMatch-len).

        // (nsupTrail0+1) last bits of supersequence matches with
        // (strMatch) last bits of str.
        supseqs_(str>>strMatch,len-strMatch,suplen-1-nsupTrail0,
            sfx|1<<(nsupTrail0+sfxlen),sfxlen+nsupTrail0+1,
            res);
    }

    // todo try pulling constants out of loops
}

// ==========

int n,b;
std::vector<char> done;
unsigned min_undone=0;

int result;
void backtrack(int nchoice){
    assert(!done[min_undone]);
    ++nchoice;
    std::vector<int> supers_s;
    for(int s:subseqs(min_undone,n,n-b)){
        // obviously (s) is not chosen. Try choosing (s)
        supers_s.clear();
        supseqs_(s,n-b,n,0,0,supers_s);
        for(unsigned i=0;i<supers_s.size();){
            int& x=supers_s[i];
            if(!done[x]){
                done[x]=true;
                ++i;
            }else{
                x=supers_s.back();
                supers_s.pop_back();
            }
        }

        unsigned old_min_undone=min_undone;
        while(true){
            if(min_undone==done.size()){
                // found !!!!
                result=std::min(result,nchoice);
                goto label1;
            }
            if(not done[min_undone])
                break;
            ++min_undone;
        }
        if(nchoice==result){
            // backtrack more will only give worse result
            goto label1;
        }

        // note that nchoice is already incremented
        backtrack(nchoice);

        label1: // undoes the effect of (above)
        for(int x:supers_s)
            done[x]=false;
        min_undone=old_min_undone;
    }
}

int main(){
    std::cin>>n>>b;

    done.resize(1<<n,0);
    result=1<<(n-b); // the actual result must be less than that

    backtrack(0);
    std::cout<<result<<'\n';
}

로컬로 실행하십시오.

[user202729@archlinux golf]$ g++ -std=c++17 -O2 delbits.cpp -o delbits
[user202729@archlinux golf]$ time for i in $(seq 1 3); do ./delbits <<< "6 $i"; done
12
4
2

real    0m0.567s
user    0m0.562s
sys     0m0.003s
[user202729@archlinux golf]$ time ./delbits <<< '7 1'
^C

real    4m7.928s
user    4m7.388s
sys     0m0.173s
[user202729@archlinux golf]$ time for i in $(seq 2 3); do ./delbits <<< "7 $i"; done
6
2

real    0m0.040s
user    0m0.031s
sys     0m0.009s

1
대부분 내 코드보다 빠르면 다른 사람들이 코드를 게시하도록 권장합니다.
user202729 년

Please? ... (참고 : 이것은 표지 설정 문제의 예입니다.)
user202729

1
작업 중입니다. 나는 그것을하는 현명한 방법을 생각해 낼 수 없습니다. 아무도 대답을 올리지 않으면 지금까지 n = 4까지만 올라갈 수 있습니다.
mypetlion 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.