Nonografik Büyütme Optimize Edici ™ Oluşturun


12

Nonogram, amacın bitişik bölgeler listesine göre siyah beyaz bir resim çizmek olduğu bir Japon bulmaca oyunudur:

"Lambda" ile örnek olmayan bir nongram.

Bir satır veya sütunun nonografik büyüklüğünü , o satır veya sütundaki bitişik siyah bölgelerin sayısı olarak tanımlayın . Örneğin, üst satırın nonografik büyüklüğü 1'dir, çünkü bu satırda 2 kareden oluşan bir bölge vardır. Sekizinci sıra 3, 2, 2, 1 olduğu için nonografik büyüklüğe sahiptir.

Boş bir satırın veya sütunun nonografik büyüklüğü 0'dır.


Göreviniz, nonogram için bir çözüm ızgarası alan ve her satır ve sütunun verilen çözüm ızgarasıyla aynı nonografik magnutide sahip olduğu mümkün olduğunca az dolu kareye sahip bir çözüm ızgarası oluşturmaktır.

Örneğin, tüm karelerin doldurulduğu nonogram ızgarası, her satırda veya sütunda nonografik büyüklüğe sahiptir:

Her karenin doldurulduğu 10x10'lik nonogram.

Aynı nonografik büyüklük, ızgarada diyagonal bir şerit bulunarak, doldurulmuş karelerin sayısını önemli ölçüde azaltarak elde edilebilir:

Yukarıdaki ile aynı nonografik büyüklüğe sahip 10x10 nonogram.


Programınız bu dosyadan 50.000 satırdan (1.32 MB tar.gz metin dosyası; 2.15 MB sıkıştırılmamış) oluşan, her biri rastgele (% 80 siyah) doldurulmuş karelere sahip tek bir 16 × 16 nonogram çözüm ızgarasını temsil eden bir giriş alacak ve her biri ilgili giriş ızgarası için optimize edilmiş çözüm ızgarasını içeren 50.000 satır daha çıktı.

Her ızgara 43 karakterli bir base64 dizesi olarak temsil edilir (kareleri soldan sağa, sonra yukarıdan aşağıya kodlama) ve programınızın çıktısını aynı biçimde döndürmesi gerekir. Örneğin, dosyadaki ilk ızgara şu E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=şekildedir:

ilk örnek nonogram

Izleme Ehangi ızgara ile başlar 000100, böylece üst satırdaki ilk altı hücre dördüncü hariç tüm beyaz. Bir sonraki karakter /hangi harita ile eşleşir 111111, bu nedenle sonraki 6 hücrenin tamamı siyahtır - vb.


Programınız, 50.000 test vakasının her biri için doğru nonografik büyüklüklere sahip bir çözüm ızgarası döndürmelidir. Daha iyi bir şey bulunmazsa girişle aynı ızgarayı döndürmeye izin verilir.

En az toplam doldurulmuş kareyi (herhangi bir dilde) döndüren program kazanır, daha kısa kod tiebreaker olur.


Mevcut skorbord:

  1. 3.637.260 - Sleafar, Java
  2. 7.270.894 - kusur, Matlab
  3. 10.239.288 - Joe Z., Bash

1
Taban 64'ün kodlama ve bununla çalışmanın gerçek bir acı olduğunu gerçekten görmüyorum. Sadece bir ve sıfır çizgileri yapmak daha kolay olmaz mıydı? Yoksa her şeyi bir bitmap olarak kodlayabilir misiniz?
Kusur

@flawr: Dosya boyutunu temel olarak azaltır (sadece 1 ve 0'lara kıyasla 6 faktör). Ayrıca, bitmap'lerle çalışmak daha da zor olurdu.
Joe Z.

Sadece siyah beyaz bir görüntü, okunması / yazılması kolay ve b64 kodlamasıyla aynı boyutta yapabilirsiniz.
Kusur

2
ayrıca giriş ve / veya çıkış için b64 kodlamasının bir fanı değil. neden i / o herhangi bir uygun formatta olsun?
Sparr

1
Varsayarsak, yarın bu zamana kadar en uygun çözüm benim olmalı.
quintopia

Yanıtlar:


7

Python 2 ve PuLP - 2.644.688 kare (en iyi şekilde küçültülmüş); 10.753.553 kare (en iyi duruma getirilmiş)

En az 1152 bayta golf yapıldı

from pulp import*
x=0
f=open("c","r")
g=open("s","w")
for k,m in enumerate(f):
 if k%2:
    b=map(int,m.split())
    p=LpProblem("Nn",LpMinimize)
    q=map(str,range(18))
    ir=q[1:18]
    e=LpVariable.dicts("c",(q,q),0,1,LpInteger)
    rs=LpVariable.dicts("rs",(ir,ir),0,1,LpInteger)
    cs=LpVariable.dicts("cs",(ir,ir),0,1,LpInteger)
    p+=sum(e[r][c] for r in q for c in q),""
    for i in q:p+=e["0"][i]==0,"";p+=e[i]["0"]==0,"";p+=e["17"][i]==0,"";p+=e[i]["17"]==0,""
    for o in range(289):i=o/17+1;j=o%17+1;si=str(i);sj=str(j);l=e[si][str(j-1)];ls=rs[si][sj];p+=e[si][sj]<=l+ls,"";p+=e[si][sj]>=l-ls,"";p+=e[si][sj]>=ls-l,"";p+=e[si][sj]<=2-ls-l,"";l=e[str(i-1)][sj];ls=cs[si][sj];p+=e[si][sj]<=l+ls,"";p+=e[si][sj]>=l-ls,"";p+=e[si][sj]>=ls-l,"";p+=e[si][sj]<=2-ls-l,""
    for r,z in enumerate(a):p+=lpSum([rs[str(r+1)][c] for c in ir])==2*z,""
    for c,z in enumerate(b):p+=lpSum([cs[r][str(c+1)] for r in ir])==2*z,""
    p.solve()
    for r in ir:
     for c in ir:g.write(str(int(e[r][c].value()))+" ")
     g.write('\n')
    g.write('%d:%d\n\n'%(-~k/2,value(p.objective)))
    x+=value(p.objective)
 else:a=map(int,m.split())
print x

(Not: yoğun girintili çizgiler boşluklarla değil sekmelerle başlar.)

Örnek çıktı: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing

Görünüşe göre gibi problemler, Tamsayı Doğrusal Programlara kolayca dönüştürülebilir ve benim de bir proje için çeşitli LP çözücüleri için bir python arayüzü olan PuLP'nin nasıl kullanılacağını öğrenmek için temel bir probleme ihtiyacım vardı. Ayrıca PuLP'nin kullanımının son derece kolay olduğu ve ungolfed LP üreticisinin ilk denediğimde mükemmel çalıştığı ortaya çıktı.

Bunu benim için çözmenin zor işini yapmak için bir şube ve bağlı IP çözücüsü kullanmakla ilgili iki güzel şey (bir şube ve bağlı çözücü uygulamak zorunda kalmak dışında)

  • Amaca uygun çözücüler gerçekten hızlı. Bu program nispeten düşük kaliteli ev bilgisayarımda yaklaşık 17 saat içinde 50000 tüm sorunları çözer. Her örnek, çözülmesi 1-1.5 saniye sürdü.
  • Garantili optimum çözümler üretir (ya da bunu yapamadıklarını söylerler). Böylece, kimsenin karelerimdeki puanı geçemeyeceğinden emin olabilirim (biri onu bağlayabilir ve golf bölümünde beni yenebilir).

Bu program nasıl kullanılır?

İlk olarak, PuLP'yi kurmanız gerekir. pip install pulppip yüklü ise hile yapmalı.

Ardından, aşağıdakileri "c" adlı bir dosyaya koymanız gerekir: https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing

Daha sonra, bu programı aynı dizinden herhangi bir geç Python 2 derlemesinde çalıştırın. Bir günden az bir sürede, her biri altında listelenen toplam doldurulmuş kare sayısı olan, 50.000 çözülmüş, program dışı ızgara (okunabilir biçimde) içeren "s" adlı bir dosyaya sahip olacaksınız.

Bunun yerine dolu karelerin sayısını en üst düzeye çıkarmak istiyorsanız, LpMinimizeon satır 8'i LpMaximizebunun yerine olarak değiştirin. Çok benzer çıktılar alacaksınız: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharing

Giriş biçimi

Bu program değiştirilmiş bir giriş formatı kullanır, çünkü Joe Z. OP'ye yaptığı bir yorumda isterseniz giriş formatını yeniden kodlamamıza izin verileceğini söyledi. Neye benzediğini görmek için yukarıdaki bağlantıyı tıklayın. Her biri 16 sayı içeren 10000 satırdan oluşur. Çift numaralı çizgiler, belirli bir örneğin satırları için büyüklüklerken, tek numaralı çizgiler, üstlerindeki çizgi ile aynı örneğin sütunları için büyüklüklerdir. Bu dosya aşağıdaki program tarafından oluşturulmuştur:

from bitqueue import *

with open("nonograms_b64.txt","r") as f:
    with open("nonogram_clues.txt","w") as g:
        for line in f:
            q = BitQueue(line.decode('base64'))
            nonogram = []
            for i in range(256):
                if not i%16: row = []
                row.append(q.nextBit())
                if not -~i%16: nonogram.append(row)
            s=""
            for row in nonogram:
                blocks=0                         #magnitude counter
                for i in range(16):
                    if row[i]==1 and (i==0 or row[i-1]==0): blocks+=1
                s+=str(blocks)+" "
            print >>g, s
            nonogram = map(list, zip(*nonogram)) #transpose the array to make columns rows
            s=""
            for row in nonogram:
                blocks=0
                for i in range(16):
                    if row[i]==1 and (i==0 or row[i-1]==0): blocks+=1
                s+=str(blocks)+" "
            print >>g, s

(Bu yeniden kodlama programı ayrıca yukarıda belirtilen aynı proje için oluşturduğum özel BitQueue sınıfımı test etmek için ekstra bir fırsat verdi. Bu, verilerin bit OR bayt dizileri olarak gönderilebildiği ve hangi verilerin bir seferde bir bit veya bir bayt atılabilir. Bu durumda, mükemmel çalıştı.)

Bir ILP oluşturmak için girdiyi yeniden kodladım, büyüklükleri oluşturmak için kullanılan ızgaralar hakkında ekstra bilgi mükemmel bir şekilde işe yaramaz. Büyüklükler tek kısıtlamadır ve bu yüzden büyüklükler erişmem gereken tek şeydir.

Ungolfed ILP oluşturucu

from pulp import *
total = 0
with open("nonogram_clues.txt","r") as f:
    with open("solutions.txt","w") as g:
        for k,line in enumerate(f):
            if k%2:
                colclues=map(int,line.split())
                prob = LpProblem("Nonogram",LpMinimize)
                seq = map(str,range(18))
                rows = seq
                cols = seq
                irows = seq[1:18]
                icols = seq[1:18]
                cells = LpVariable.dicts("cell",(rows,cols),0,1,LpInteger)
                rowseps = LpVariable.dicts("rowsep",(irows,icols),0,1,LpInteger)
                colseps = LpVariable.dicts("colsep",(irows,icols),0,1,LpInteger)
                prob += sum(cells[r][c] for r in rows for c in cols),""
                for i in rows:
                    prob += cells["0"][i] == 0,""
                    prob += cells[i]["0"] == 0,""
                    prob += cells["17"][i] == 0,""
                    prob += cells[i]["17"] == 0,""
                for i in range(1,18):
                    for j in range(1,18):
                        si = str(i); sj = str(j)
                        l = cells[si][str(j-1)]; ls = rowseps[si][sj]
                        prob += cells[si][sj] <= l + ls,""
                        prob += cells[si][sj] >= l - ls,""
                        prob += cells[si][sj] >= ls - l,""
                        prob += cells[si][sj] <= 2 - ls - l,""
                        l = cells[str(i-1)][sj]; ls = colseps[si][sj]
                        prob += cells[si][sj] <= l + ls,""
                        prob += cells[si][sj] >= l - ls,""
                        prob += cells[si][sj] >= ls - l,""
                        prob += cells[si][sj] <= 2 - ls - l,""
                for r,clue in enumerate(rowclues):
                    prob += lpSum([rowseps[str(r+1)][c] for c in icols]) == 2 * clue,""
                for c,clue in enumerate(colclues):
                    prob += lpSum([colseps[r][str(c+1)] for r in irows]) == 2 * clue,""
                prob.solve()
                print "Status for problem %d: "%(-~k/2),LpStatus[prob.status]
                for r in rows[1:18]:
                    for c in cols[1:18]:
                        g.write(str(int(cells[r][c].value()))+" ")
                    g.write('\n')
                g.write('Filled squares for %d: %d\n\n'%(-~k/2,value(prob.objective)))
                total += value(prob.objective)
            else:
                rowclues=map(int,line.split())
print "Total number of filled squares: %d"%total

Bu aslında yukarıda bağlantılı "örnek çıktı" üreten programdır. Bu nedenle, golf yaparken kesildiğim her ızgaranın sonundaki ekstra uzun teller. (Golfçü versiyon, kelimeler eksi aynı çıktı üretmelidir "Filled squares for ")

Nasıl çalışır

cells = LpVariable.dicts("cell",(rows,cols),0,1,LpInteger)
rowseps = LpVariable.dicts("rowsep",(irows,icols),0,1,LpInteger)
colseps = LpVariable.dicts("colsep",(irows,icols),0,1,LpInteger)

18x18 ızgara kullanıyorum, merkez 16x16 kısmı gerçek bulmaca çözümü. cellsbu ızgara. İlk satır 324 ikili değişken oluşturur: "cell_0_0", "cell_0_1" vb. Ayrıca, ızgaranın çözüm kısmındaki hücreler arasında ve etrafındaki "boşlukların" ızgaralarını oluşturuyorum. rowsepshücreleri yatay olarak ayıran boşlukları simgeleyen 289 değişkeni gösterirken, colsepshücreleri dikey olarak ayıran boşlukları işaretleyen değişkenleri gösterir. İşte bir unicode diyagramı:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0S ve s tarafından izlenen ikili değerler celldeğişkenler, |s ikili değerler tarafından izlenen olan rowsepdeğişkenleri ve -s ikili değerler tarafından izlenen olan colsepdeğişkenler.

prob += sum(cells[r][c] for r in rows for c in cols),""

Amaç işlevi budur. Sadece tüm celldeğişkenlerin toplamı . Bunlar ikili değişkenler olduğundan, bu tam olarak çözümdeki doldurulmuş karelerin sayısıdır.

for i in rows:
    prob += cells["0"][i] == 0,""
    prob += cells[i]["0"] == 0,""
    prob += cells["17"][i] == 0,""
    prob += cells[i]["17"] == 0,""

Bu sadece ızgaranın dış kenarı etrafındaki hücreleri sıfıra ayarlar (bu yüzden onları yukarıda sıfır olarak temsil ettim). Bu, kaç hücre bloğunun dolduğunu izlemenin en uygun yoludur, çünkü doldurulmadan doldurulan (bir sütun veya satır boyunca hareket eden) her değişikliğin doldurulmuştan doldurulmamışa (veya tersi) karşılık gelen bir değişiklikle eşleşmesini sağlar. ), satırdaki ilk veya son hücre doldurulmuş olsa bile. İlk etapta 18x18 ızgara kullanmanın tek nedeni budur. Blokları saymanın tek yolu bu değil, ama bence en basit olanı bu.

for i in range(1,18):
    for j in range(1,18):
        si = str(i); sj = str(j)
        l = cells[si][str(j-1)]; ls = rowseps[si][sj]
        prob += cells[si][sj] <= l + ls,""
        prob += cells[si][sj] >= l - ls,""
        prob += cells[si][sj] >= ls - l,""
        prob += cells[si][sj] <= 2 - ls - l,""
        l = cells[str(i-1)][sj]; ls = colseps[si][sj]
        prob += cells[si][sj] <= l + ls,""
        prob += cells[si][sj] >= l - ls,""
        prob += cells[si][sj] >= ls - l,""
        prob += cells[si][sj] <= 2 - ls - l,""

Bu, ILP mantığının gerçek etidir. Temel olarak, her bir hücrenin (ilk satır ve sütundaki hücreler dışında), hücrenin ve ayırıcısının doğrudan satırında soluna ve doğrudan sütunun üstünde mantıksal xor olmasını gerektirir. Bu harika yanıttan {0,1} tam sayı programı içinde bir xor'u simüle eden kısıtlamaları aldım: /cs//a/12118/44289

Biraz daha açıklamak gerekirse: bu xor kısıtlaması, ayırıcıların 1 ve sadece 0 ve 1 olan hücreler arasında yer almaları durumunda 1 olmasını sağlar (doldurulmadan doldurulmaya veya tam tersi). Böylece, bir satır veya sütunda, o satır veya sütundaki blok sayısının tam iki katı kadar 1 değerli ayırıcı olacaktır. Başka bir deyişle, belirli bir satır veya sütundaki ayırıcıların toplamı, o satırın / sütunun büyüklüğünün tam olarak iki katıdır. Bu nedenle aşağıdaki kısıtlamalar:

for r,clue in enumerate(rowclues):
    prob += lpSum([rowseps[str(r+1)][c] for c in icols]) == 2 * clue,""
for c,clue in enumerate(colclues):
    prob += lpSum([colseps[r][str(c+1)] for r in irows]) == 2 * clue,""

Ve işte bu kadar. Geri kalanlar varsayılan çözücüden ILP'yi çözmesini ister, daha sonra elde edilen çözümü dosyaya yazarken biçimlendirir.


Gerçekten iyi bir cevap. LP çözücüler hakkında bilgi edinmek istiyorum. Eğer bir 19x19, 6 renk kurulu (bir çözüm hesaplamak için zamanla ilgili ) için sel bulmaca (bağlantı) çözmek için kullanılabilir düşünüyor musunuz ? Zaten bu yarışmaya cevap verdim (ve kazandım) ancak yöntemim (A * arama algoritması) sadece optimal olmayan çözümler veriyor.
tigrou

@tigrou Teşekkürler. Sel sorununun böyle bir çözümü kabul edecek kadar doğrusal olduğundan emin değilim. Kesinlikle bu şekilde nasıl modelleneceğini göremiyorum.
quintopia

Birisi zaten denedi gibi görünüyor: kunigami.blog/2012/09/16/flood-it-an-exact-approach Ancak 14x14 bir tahta için uygun zamanda en uygun çözümleri alamadılar.
tigrou

3

Java, 6.093.092 4.332.656 3.637.260 kare (küçültülmüş), 10.567.550 10.567.691 10.568.746 kare (büyütülmüş)

Programın her iki varyantı da, büyüklüğü değiştirmeden kaynak ızgara üzerinde işlemleri tekrar tekrar gerçekleştirir.

Minimizer

) (Küçültmek

resim açıklamasını buraya girin

Siyah bir karede 90 ° açıda 2 beyaz komşu ve 2 siyah komşu varsa, beyaz bir kare ile değiştirilebilir.

moveLine ()

resim açıklamasını buraya girin resim açıklamasını buraya girin

Yukarıdaki gibi yapılandırmalarda siyah çizgi sağa taşınabilir. Bu, yeni büzülme olasılıklarını açmak için 4 hat yönünün tümü için saat yönünde ve saat yönünün tersine tekrar tekrar yapılır.

Maximizer

main()Bu sürüm için satırı aç ve üstündeki satırı yorumla.

) (Büyümek

resim açıklamasını buraya girin

Beyaz bir karede 90 ° açıda 2 beyaz komşu ve 2 siyah komşu varsa, siyah bir kare ile değiştirilebilir.

moveLine ()

Minimizer ile aynı.

Kaynak

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.Base64;
import java.util.function.Function;

public class Main {
    private static final int SIZE = 16;
    private static final int SIZE_4 = SIZE + 4;
    private static final int E = 0;
    private static final int N = 1;
    private static final int W = 2;
    private static final int S = 3;

    private static final Base64.Decoder decoder = Base64.getMimeDecoder();
    private static final Base64.Encoder encoder = Base64.getMimeEncoder();
    private static int sourceBlack = 0;
    private static int targetBlack = 0;

    private static class Nonogram {
        private final boolean[] cells = new boolean[SIZE_4 * SIZE_4];
        private final int[] magnitudes;

        public Nonogram(String encoded) {
            super();
            byte[] decoded = decoder.decode(encoded);
            for (int i = 0; i < decoded.length; ++ i) {
                for (int j = 0; j < 8; ++ j) {
                    if ((decoded[i] & (1 << (7 - j))) != 0) {
                        int k = i * 8 + j;
                        cells[getPos(k / SIZE, k % SIZE)] = true;
                        ++ sourceBlack;
                    }
                }
            }
            magnitudes = calcMagnitudes();
        }

        private int getPos(int row, int col) {
            return (row + 2) * SIZE_4 + col + 2;
        }

        private int move(int pos, int dir, int count) {
            switch (dir) {
                case E: return pos + count;
                case N: return pos - count * SIZE_4;
                case W: return pos - count;
                case S: return pos + count * SIZE_4;
                default: return pos;
            }
        }

        private int move(int pos, int dir) {
            return move(pos, dir, 1);
        }

        private int[] calcMagnitudes() {
            int[] result = new int[SIZE * 2];
            for (int row = 0; row < SIZE; ++ row) {
                for (int col = 0; col < SIZE; ++ col) {
                    int pos = getPos(row, col);
                    if (cells[pos]) {
                        if (!cells[move(pos, W)]) {
                            ++ result[row + SIZE];
                        }
                        if (!cells[move(pos, N)]) {
                            ++ result[col];
                        }
                    }
                }
            }
            return result;
        }

        private boolean isBlack(int pos) {
            return cells[pos];
        }

        private boolean isWhite(int pos) {
            return !cells[pos];
        }

        private boolean allBlack(int pos, int dir, int count) {
            int p = pos;
            for (int i = 0; i < count; ++ i) {
                if (isWhite(p)) {
                    return false;
                }
                p = move(p, dir);
            }
            return true;
        }

        private boolean allWhite(int pos, int dir, int count) {
            int p = pos;
            for (int i = 0; i < count; ++ i) {
                if (isBlack(p)) {
                    return false;
                }
                p = move(p, dir);
            }
            return true;
        }

        private int findWhite(int pos, int dir) {
            int count = 0;
            int p = pos;
            while (cells[p]) {
                ++ count;
                p = move(p, dir);
            }
            return count;
        }

        @SafeVarargs
        private final void forEach(Function<Integer, Boolean>... processors) {
            outer:
            for (;;) {
                for (Function<Integer, Boolean> processor : processors) {
                    for (int row = 0; row < SIZE; ++ row) {
                        for (int col = 0; col < SIZE; ++ col) {
                            if (processor.apply(getPos(row, col))) {
                                continue outer;
                            }
                        }
                    }
                }
                return;
            }
        }

        private boolean shrink(int pos) {
            if (cells[pos] && cells[move(pos, W)] != cells[move(pos, E)] &&
                    cells[move(pos, N)] != cells[move(pos, S)]) {
                cells[pos] = false;
                return true;
            }
            return false;
        }

        private boolean grow(int pos) {
            if (!cells[pos] && cells[move(pos, W)] != cells[move(pos, E)] &&
                    cells[move(pos, N)] != cells[move(pos, S)]) {
                cells[pos] = true;
                return true;
            }
            return false;
        }

        private boolean moveLine(boolean clockwise, int dir, int sourcePos) {
            int from = (dir + (clockwise ? 1 : 3)) % 4;
            int to = (dir + (clockwise ? 3 : 1)) % 4;
            int opp = (dir + 2) % 4;
            if (isBlack(sourcePos) && isWhite(move(sourcePos, from)) && isWhite(move(sourcePos, dir))) {
                int toCount = findWhite(move(move(sourcePos, dir), to), to) + 1;
                if (allWhite(move(sourcePos, to), to, toCount + 1)) {
                    int lineCount = 1;
                    int tmpPos = move(sourcePos, opp);
                    while (isBlack(tmpPos) && isWhite(move(tmpPos, from)) && allWhite(move(tmpPos, to),  to, toCount + 1)) {
                        ++ lineCount;
                        tmpPos = move(tmpPos, opp);
                    }
                    if (allBlack(tmpPos, to, toCount + 1)) {
                        tmpPos = sourcePos;
                        for (int j = 0; j < lineCount; ++ j) {
                            cells[tmpPos] = false;
                            cells[move(tmpPos, to, toCount)] = true;
                            tmpPos = move(tmpPos, opp);
                        }
                        return true;
                    }
                }
            }
            return false;
        }

        public Nonogram minimize() {
            for (int i = 0; i < 5; ++ i) {
                forEach(pos -> shrink(pos), pos -> moveLine(true, E, pos), pos -> moveLine(true, N, pos),
                        pos -> moveLine(true, W, pos), pos -> moveLine(true, S, pos));
                forEach(pos -> shrink(pos), pos -> moveLine(false, E, pos), pos -> moveLine(false, N, pos),
                        pos -> moveLine(false, W, pos), pos -> moveLine(false, S, pos));
            }
            return this;
        }

        public Nonogram maximize() {
            for (int i = 0; i < 5; ++ i) {
                forEach(pos -> grow(pos), pos -> moveLine(true, E, pos), pos -> moveLine(true, N, pos),
                        pos -> moveLine(true, W, pos), pos -> moveLine(true, S, pos));
                forEach(pos -> grow(pos), pos -> moveLine(false, E, pos), pos -> moveLine(false, N, pos),
                        pos -> moveLine(false, W, pos), pos -> moveLine(false, S, pos));
            }
            return this;
        }

        public String toBase64() {
            if (!Arrays.equals(magnitudes, calcMagnitudes())) {
                throw new RuntimeException("Something went wrong!");
            }
            byte[] decoded = new byte[SIZE * SIZE / 8];
            for (int i = 0; i < decoded.length; ++ i) {
                for (int j = 0; j < 8; ++ j) {
                    int k = i * 8 + j;
                    if (cells[getPos(k / SIZE, k % SIZE)]) {
                        decoded[i] |= 1 << (7 - j);
                        ++ targetBlack;
                    }
                }
            }
            return encoder.encodeToString(decoded);
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            for (int row = 0; row < SIZE; ++ row) {
                for (int col = 0; col < SIZE; ++ col) {
                    b.append(cells[getPos(row, col)] ? '#' : ' ');
                }
                b.append('\n');
            }
            return b.toString();
        }
    }

    public static void main(String[] args) throws Exception {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("solutions_b64.txt"));
                BufferedReader reader = new BufferedReader(new FileReader("nonograms_b64.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(new Nonogram(line).minimize().toBase64() + "\n");
                //writer.write(new Nonogram(line).maximize().toBase64() + "\n");
            }
        }
        System.out.printf("%d -> %d", sourceBlack, targetBlack);
    }
}

1

Bash - 10.239.288 kare

Son yer referans çözümü olarak:

cp nonograms_b64.txt solutions_b64.txt

Programınızın daha iyi bir çözüm bulamaması durumunda aynı ızgarayı döndürmesine izin verildiğinden, dosyanın tamamını yazdırmak da geçerlidir.

Test dosyasında her biri 256 kare olan 50.000 ızgaradan doldurulan karelerin% 80'inden beklediğiniz 10.240.000'e oldukça yakın olan toplam 10.239.288 siyah kare var. Test bataryası sorularımda her zamanki gibi, bu sefer skorların 4 veya 5 milyona yakın olacağından şüphelenmeme rağmen, en uygun skorların 2 milyon aralığında olacağı beklentisiyle test vakalarının sayısını seçtim. .


Siyah kareleri en aza indirmek yerine en üst düzeye çıkaran ve 10.240.000'i aşmayı başaran bir çözüm yaratabilecek olan biri varsa , bunu bir ödül vermeyi düşünebilirim.


1

Matlab, 7.270.894 kare (orijinalin ~% 71'i)

Fikir, basit bir tekrarlanan açgözlü aramadır: Her siyah kare için, nonografik büyüklükleri değiştirmeden beyaza ayarlayıp ayarlayamayacağınızı deneyin. Bunu iki kez tekrarlayın. (Daha fazla tekrarla daha iyi sonuçlar elde edebilirsiniz, ancak ücretsiz değil: Bu, daha uzun bir çalışma süresine neden olur. Şimdi yaklaşık 80 dakika idi. Bunu yapardım, eğer 50k test vakalarının tümünü hesaplamak zorunda kalmazsak ...)

Burada kod (her işlev her zamanki gibi ayrı bir dosyada.)

function D = b64decode(E)
% accepts a string of base 64 encoded data, and returns a array of zeros
% and ones
F = E;
assert( mod(numel(E),4)==0 && 0 <= sum(E=='=') && sum(E=='=') <= 2,'flawed base 64 code')

F('A' <= E & E<= 'Z') = F('A' <= E & E<= 'Z') -'A';       %upper case
F('a' <= E & E<= 'z') = F('a' <= E & E<= 'z') -'a' + 26;  %lower case
F('0'<= E & E <= '9') = F('0'<= E & E <= '9') -'0' + 52;  %digits
F( E == '+') = 62;
F( E == '/') = 63;
F( E == '=') = 0;

D=zeros(1,numel(E)*3*8/4);

for k=1:numel(E);
    D(6*(k-1)+1 + (0:5)) = dec2bin(F(k),6)-'0';
end

if E(end) == '=';
    D(end-7:end) = [];
    if E(end-1) == '=';
        D(end-7:end) = [];
    end
end
end

function E = b64encode(D)
assert(mod(numel(D),8)==0,'flawed byte code');
N=0;
while mod(numel(D),6) ~= 0;
    D = [D,zeros(1,8)];
    N = N+1;
end
dict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

E=zeros(1,numel(D)/6)+'=';
for k=0:numel(E)-N-1;
    E(k+1) = dict(bin2dec( [D(6*k+(1:6))+'0',''] ) + 1);
end

E = [E,''];
end


function [B,T,O] = reduce_greedy(N)
% reduce greedily
NM = nomographic_magnitude(N);
B = N; %current best
M = N;
T = nnz(N); %current number of filled squares
O = T;

for r=1:2;  %how many repetitions
    I = find(B);
    for k=1:numel(I);
        M = B;
        M( I(k) ) = 0;
        %check whether we have a valid solution
        if all(NM == nomographic_magnitude(M))
            if T > nnz(M); %did we actually reduce the number of filled squares?
                B = M;
                T = nnz(M);
            end
        end
    end
end


%% main file
string_in = fileread('nonograms_b64.txt');
string_out = string_in;
tic
total_new = 0;  %total number of black squares
total_old = 0;
M = 50000;
for k=1:M;
    disp(k/M); %display the progress
    line = string_in(45*(k-1)+(1:44));
    decoded = b64decode(line);        
    nonogram = reshape(decoded,16,[]) ;%store nonogram as a matrix
    [B,T,O] = reduce_greedy(nonogram);
    disp([nnz(B),nnz(nonogram)])
    total_new = total_new + T;
    total_old = total_old + O;
    string_in(45*(k-1)+(1:44)) = b64encode(B(:).');
end
toc
F = fopen('nonograms_b64_out.txt','w');
fprintf(F,string_out);
%%
disp([total_new,total_old])
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.