Biraz bit sil ve say


26

Tüm 2^nfarklı uzunluktaki dizeleri dikkate alın nve varsayalım n > 2. b < n/2İkili dizgilerin her birinden tam olarak bit silme ve n-bkalan uzunluk dizeleri bırakma izniniz var . Kalan farklı dizelerin sayısı, hangi bitleri sildiğinize bağlıdır. Amacınızın mümkün olduğunca az sayıda farklı dizge bırakmak olduğunu varsayalım, bu zorluk bir işlev olarak ne kadar az kalabileceğinizi hesaplamak için kod yazmaktır n.

Örnek, n=3ve b = 1. Sadece iki dizeyi bırakabilirsiniz 11ve 00.

İçin n=9ve b = 1,2,3,4elimizdeki70,18,6,2

İçin n=8ve b = 1,2,3elimizdeki40,10,4

İçin n=7ve b = 1,2,3elimizdeki20,6,2

İçin n=6ve b = 1,2elimizdeki12,4

İçin n=5ve b = 1,2elimizdeki6,2

Bu soru aslen farklı bir formda 2014 yılında tarafımdan ortaya atıldığını MO .

Giriş ve çıkış

Kodunuz bir tamsayı almalı nve bbaşlangıç b = 0ve artışın her değeri için tek bir tamsayı çıkarmalıdır .

Gol

Linux tabanlı bilgisayarımda puanınız bir dakikadan az bir sürede nkodunuzun tamamlandığı en büyük puandır b < n/2. Beraberlik durumunda b, ortak en büyük nkazançlar için kodunuzun en büyüğü olur . Beraberinde bu kritere uyulmaması durumunda, en büyük değerler için en hızlı kod nve bkarar verilir. Zamanlar birbirinin ikinci veya iki içindeyse, ilk gönderilen cevap kazanır.

Diller ve kütüphaneler

İstediğiniz kütüphane dilini kullanabilirsiniz. Kodunuzu çalıştırmam gerektiğinden, ücretsiz olsaydı (birada olduğu gibi) ve Linux'ta çalışsa yardımcı olurdu.


b > 0Ek girdi gereksinimi olarak kabul ediyorum ? Ya olur n=3ve b=0sadece çıkış 2^nsonuç olarak?
Kevin Cruijssen

@KevinCruijssen Gerçekten de vermesi gerekiyor 2^n.
Anush

Ayrıca, girişin tekli nve tekli olduğunu söylüyorsunuz b, ancak puan bir dakikadan az bir sürede nkodun tamamlandığı en büyük puandır b < n/2. nBu durumda tek bir giriş yapmak ve bunun için tüm sonuçları almak daha iyi olmaz mıydı 0 <= b < n/2? Ya da iki program / fonksiyonları sağlamalıdır: Bir iki girdileri alarak nve bve bir tek girişi alarak nve aralığındaki tüm sonuçları çıkışının 0 <= b < n/2?
Kevin Cruijssen

2
Ben zaten senin zorluğunu aşmıştım, bir daha yapamam. :) Bunu nasıl verimli bir şekilde hesaplayabileceğimi bilmeme rağmen (verimli O algoritmaları her zaman kötüye gittiğim bir şeydi .. ve BT üniversitesinde birkaç kez yinelemek zorunda kaldığım birkaç konudan biriydi) çok ilginç bir mücadele. İnsanların ne gibi cevaplar bulduğunu merak ediyorum
Kevin Cruijssen

2
Çalışan bir örnek var mı? Hem doğruluk açısından hem de hızın karşılaştırılması için başlamak için iyi bir yer olacaktır.
maksb

Yanıtlar:


6

Python 2.7 / Gurobi n = 9

Bu çözüm, Gurobi'nin ILP çözücüsünün boolean Karma-Tamsayılı Problemleri (MIP) için kullanımı oldukça kolaydır.

Tek hile, problem boyutlarını yarıya indirmek için 1'in tamamlayıcısında simetriyi çıkarmaktır.

Gurobi LLC'nin sınırlı "ücretsiz" lisansını kullanarak, 2000 kısıtlama ile sınırlandırıldık, ancak 10 del 1'i çözmek, dizüstü bilgisayarımda zaten 60 saniyelik zaman sınırının dışında.

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)

GÜNCELLEME + CORR: 10,2 optimum çözelti büyüklüğü 31'e sahip (örneğin bakınız) Gurobi, 30 büyüklüğünde simetrik bir çözüm olmadığını gösteriyor (problemi geri döndürüyor). tam sayı kalıpları 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 255veya0 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


"En hızlı iddia edilen sonsuz ödül" rekorunu mu kırdın?
user202729

Burada hiçbir ödül göremiyorum, ne demek istiyorsun?
jayprich

@ user202729 Evet .. Çok düşük ayarladım. N = 10 :) 'a ayarlamalıydım :)
Anush

Aslında n = 9'da çözmek kolay bir şey değil. Bu nedenle OP mevcut bir kütüphaneyi kullanıyor (ki benimki gibi elle yazılmış bir çözümden daha iyi olması gerekiyordu).
user202729

1
Thanks @ChristianSievers MO görüyorum ki 10,2 sadece çürütmediğim ya da doğrulayamadığım asimetrik optima var. N = 9'a kadar çalışan simetri varsayım kısayolunu kaldırırsam, Gurobi'nin gereken sürede hala n = 9'a kadar çözebileceği anlaşılıyor.
jayprich

3

C ++, n = 6

Bazı küçük optimizasyonlarla kaba kuvvet.

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

Yerel olarak çalıştır:

[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
Çoğunlukla, benimkinden daha hızlıysa, diğerlerini kodlarını göndermeye teşvik etmek.
user202729

Lütfen? ... (not: Bu, ayarlanmış bir kapak sorununun bir örneğidir.)
user202729

1
Üzerinde çalışıyorum. Bunu yapmanın akıllıca bir yolunu bulamıyorum. Başka kimse cevap vermezse, benim için şimdiye kadar n = 4 seviyesine kadar çıkabilecek olanı koyacağım.
Mypetlion
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.