Elimina alcuni bit e conta


26

Considerare tutte le 2^ndiverse stringhe binarie di lunghezza ne assumere n > 2. È consentito eliminare esattamente i b < n/2bit da ciascuna delle stringhe binarie, rimanendo stringhe di lunghezza n-brimanenti. Il numero di stringhe distinte rimanenti dipende dai bit eliminati. Supponendo che il tuo obiettivo sia quello di lasciare il minor numero possibile di stringhe diverse, questa sfida è quella di scrivere un codice per calcolare quanto ne puoi lasciare in funzione n.

Esempio n=3e b = 1. Puoi lasciare solo le due stringhe 11e 00.

Per n=9e b = 1,2,3,4abbiamo70,18,6,2

Per n=8e b = 1,2,3abbiamo40,10,4

Per n=7e b = 1,2,3abbiamo20,6,2

Per n=6e b = 1,2abbiamo12,4

Per n=5e b = 1,2abbiamo6,2

Questa domanda è stata inizialmente posta da me nel 2014 in una forma diversa su MO .

Ingresso e uscita

Il codice deve contenere un numero intero ne generare un singolo numero intero per ciascun valore di binizio b = 0e di incremento.

Punto

Il tuo punteggio è il più grande nper il quale il tuo codice viene completato per tutti b < n/2in meno di un minuto sul mio PC basato su Linux. In caso di pareggio, il massimo che il btuo codice ottiene per le nvincite più grandi congiunte . In caso di pareggio si rompe anche su questo criterio, il codice più veloce per i valori più grandi di ne bdecide. Se i tempi sono entro un secondo o due l'uno dall'altro, la prima risposta pubblicata vince.

Lingue e biblioteche

Puoi usare qualsiasi lingua della biblioteca che ti piace. Poiché devo eseguire il tuo codice, sarebbe di aiuto se fosse gratuito (come nella birra) e funzionasse in Linux.


Sto assumendo b > 0come requisito di input aggiuntivo? O sarebbe n=3e b=0semplicemente produrre 2^ncome risultato?
Kevin Cruijssen,

@KevinCruijssen Dovrebbe 2^ndavvero uscire .
Anush,

Inoltre, dici che l'input è un singolo ne uno singolo b, ma il punteggio è il più grande nper il quale il codice completa tutto b < n/2in meno di un minuto. Non sarebbe meglio avere un singolo input nin quel caso e produrre tutti i risultati per 0 <= b < n/2? O dovremmo fornire due programmi / funzioni: uno prendendo due ingressi ne b, e uno prendendo solo l'ingresso ne l'output di tutti i risultati della gamma 0 <= b < n/2?
Kevin Cruijssen,

2
Bene, avevo già votato per la tua sfida, quindi non posso farlo di nuovo. :) Anche se non ho idea di come calcolare questo in modo efficiente (gli algoritmi O efficienti erano qualcosa in cui sono sempre stato cattivo in .. e una delle poche materie al college IT che ho dovuto ripetere un paio di volte), sembra che una sfida molto interessante. Sono curioso di vedere quali risposte le persone escogitano.
Kevin Cruijssen,

2
C'è un esempio funzionante? Sarebbe un buon punto di partenza, sia in termini di correttezza, sia in termini di confronto della velocità.
maxb

Risposte:


6

Python 2.7 / Gurobi n = 9

Questa soluzione è un uso molto diretto del risolutore ILP di Gurobi per i problemi booleani di valori misti interi (MIP).

L'unico trucco è eliminare la simmetria nel complemento di 1 per dimezzare le dimensioni del problema.

Utilizzando la licenza "gratuita" a tempo limitato di Gurobi LLC siamo limitati a 2000 vincoli, ma la risoluzione di 10 del 1 è ben al di fuori del limite di 60 secondi sul mio laptop.

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)

AGGIORNAMENTO + CORR: 10,2 ha una dimensione ottimale della soluzione 31 (vedi ad es.) Gurobi non mostra una soluzione simmetrica della dimensione 30 (restituisce un problema non realizzabile) .. [il mio tentativo di mostrare la fattibilità asimmetrica a 30 è rimasto inconcludente dopo 9,5 ore] ad es. Bit modelli di numeri interi 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 255o0 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


Hai battuto il record di "ricompensa infinita rivendicata più velocemente"?
user202729

Non vedo nessuna taglia qui, cosa intendi?
jayprich,

@ user202729 Sì .. L'ho impostato troppo basso. Avrei dovuto impostarlo su n = 10 :)
Anush il

In realtà risolverlo a n = 9 non è una cosa facile. Ecco perché OP utilizza una libreria esistente (che dovrebbe essere migliore di una soluzione scritta a mano, come la mia).
user202729

1
Grazie @ChristianSievers Vedo che MO afferma che 10,2 ha solo optima asimmetrici che non posso confutare né verificare. Se rimuovo il collegamento all'assunzione di simmetria che funziona fino a n = 9 si scopre che Gurobi può ancora risolvere fino a n = 9 nel tempo richiesto.
jayprich,

3

C ++, n = 6

Forza bruta con alcune piccole ottimizzazioni.

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

Esegui localmente:

[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
Principalmente per incoraggiare gli altri a pubblicare il proprio codice se è più veloce del mio.
user202729

Per favore? ... (nota: questa è un'istanza di un problema con la copertina impostata).
user202729

1
Ci sto lavorando. Non riesco proprio a trovare un modo intelligente di farlo. Se nessun altro invia una risposta, metterò la mia che può arrivare fino a n = 4 finora.
mypetlion,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.