Xóa một số bit và đếm


26

Xem xét tất cả 2^ncác chuỗi nhị phân khác nhau của chiều dài nvà giả định n > 2. Bạn được phép xóa chính xác b < n/2các bit từ mỗi chuỗi nhị phân, để lại các chuỗi có độ dài n-bcòn lại. Số lượng các chuỗi riêng biệt còn lại phụ thuộc vào bit bạn xóa. Giả sử mục tiêu của bạn là để lại càng ít chuỗi khác nhau càng tốt, thử thách này là viết mã để tính toán số lượng bạn có thể để lại như một hàm của n.

Ví dụ, n=3b = 1. Bạn chỉ có thể để lại hai chuỗi 1100.

Cho n=9b = 1,2,3,4chúng ta có70,18,6,2

Cho n=8b = 1,2,3chúng ta có40,10,4

Cho n=7b = 1,2,3chúng ta có20,6,2

Cho n=6b = 1,2chúng ta có12,4

Cho n=5b = 1,2chúng ta có6,2

Câu hỏi này ban đầu được tôi đặt ra vào năm 2014 dưới một hình thức khác trên MO .

Đầu vào và đầu ra

Mã của bạn sẽ lấy một số nguyên nvà xuất ra một số nguyên duy nhất cho mỗi giá trị bbắt đầu b = 0và tăng dần.

Ghi bàn

Điểm của bạn là lớn nhất nmà mã của bạn hoàn thành b < n/2trong vòng một phút trên PC dựa trên Linux của tôi. Trong trường hợp hòa vốn, bmã lớn nhất của bạn sẽ giành được cho phần nthắng lớn nhất . Trong trường hợp hòa vốn phá vỡ theo tiêu chí đó, mã nhanh nhất cho các giá trị lớn nhất nbquyết định. Nếu thời gian trong vòng một hoặc hai giây với nhau, câu trả lời được đăng đầu tiên sẽ thắng.

Ngôn ngữ và thư viện

Bạn có thể sử dụng bất kỳ ngôn ngữ của thư viện bạn thích. Bởi vì tôi phải chạy mã của bạn, nó sẽ giúp ích nếu nó miễn phí (như trong bia) và hoạt động trong Linux.


Tôi đang giả sử b > 0như là yêu cầu đầu vào bổ sung? Hoặc sẽ n=3b=0đơn giản là đầu ra 2^nnhư kết quả?
Kevin Cruijssen

@KevinCruijssen Nó 2^nthực sự nên xuất ra .
Anush

Ngoài ra, bạn nói đầu vào là một nvà một b, nhưng điểm số là lớn nhất nmà mã hoàn thành tất cả b < n/2trong vòng một phút. Sẽ không tốt hơn nếu có một đầu vào ntrong trường hợp đó và xuất tất cả kết quả cho 0 <= b < n/2? Hoặc chúng ta nên cung cấp hai chương trình / chức năng: một dùng hai đầu vào nb, và một dùng chỉ đầu vào nvà xuất ra tất cả các kết quả trong phạm vi 0 <= b < n/2?
Kevin Cruijssen

2
Chà, tôi đã nâng cao thách thức của bạn, vì vậy không thể làm lại. :) Mặc dù tôi không biết làm thế nào để tính toán hiệu quả này (thuật toán O hiệu quả là thứ mà tôi luôn kém .. và một trong số ít môn học ở trường đại học CNTT tôi phải làm lại một vài lần), có vẻ như một thử thách rất thú vị Tôi tò mò muốn xem câu trả lời của mọi người.
Kevin Cruijssen

2
Có một ví dụ làm việc? Nó sẽ là một nơi tốt để bắt đầu, cả về tính chính xác, nhưng cũng để so sánh tốc độ.
maxb

Câu trả lời:


6

Con trăn 2.7 / Gurobi n = 9

Giải pháp này là cách sử dụng bộ giải ILP của Gurobi rất thẳng cho các bài toán tích phân hỗn hợp boolean (MIP).

Bí quyết duy nhất là lấy ra sự đối xứng trong phần bù của 1 để giảm một nửa kích thước bài toán.

Sử dụng giấy phép "miễn phí" giới hạn thời gian của Gurobi LLC, chúng tôi bị giới hạn ở 2000 ràng buộc, nhưng việc giải quyết 10 del 1 vẫn nằm ngoài giới hạn thời gian 60 giây trên máy tính xách tay của tôi.

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)

CẬP NHẬT + CORR: 10,2 có kích thước giải pháp tối ưu 31 (xem ví dụ) Gurobi cho thấy không có giải pháp đối xứng nào có kích thước 30 (trả về vấn đề không khả thi) .. [cố gắng của tôi để hiển thị tính khả thi không đối xứng ở 30 vẫn không kết luận sau thời gian chạy 9,5 giờ] mô hình số nguyên 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 255hoặc0 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


Bạn đã phá vỡ kỷ lục "tiền thưởng vô hạn được yêu cầu nhanh nhất"?
dùng202729

Tôi không thấy tiền thưởng ở đây, ý bạn là gì?
jayprich

@ user202729 Có .. Tôi đặt nó quá thấp. Tôi nên đặt nó ở n = 10 :)
Anush

Trên thực tế, giải quyết nó ở n = 9 không phải là một điều dễ dàng. Đó là lý do tại sao OP sử dụng một thư viện hiện có (được cho là tốt hơn giải pháp viết tay, như của tôi).
dùng202729

1
Cảm ơn @ChristianSievers Tôi thấy MO tuyên bố rằng 10,2 chỉ có tối ưu không đối xứng mà tôi không thể bác bỏ cũng không xác minh. Nếu tôi loại bỏ lối tắt giả định đối xứng hoạt động tới n = 9 thì hóa ra Gurobi vẫn có thể giải quyết tới n = 9 trong thời gian cần thiết.
jayprich

3

C ++, n = 6

Lực lượng vũ phu với một số tối ưu hóa nhỏ.

#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';
}

Chạy cục bộ:

[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
Chủ yếu là để khuyến khích người khác đăng mã của họ nếu nó nhanh hơn mã của tôi.
dùng202729

Xin vui lòng? ... (lưu ý: Đây là một ví dụ về sự cố bao gồm tập hợp.)
user202729

1
Tôi đang giải quyết nó. Tôi không thể nghĩ ra bất kỳ cách làm thông minh nào. Nếu không có ai khác trả lời, tôi sẽ đưa ra câu trả lời chỉ có thể cao tới n = 4 cho đến nay.
mypetlion
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.