Latin kare sıkıştırma


31

Bir Latin kare hiçbir satır veya sütun sembolleri tekrarladı bir karedir: .

13420
21304
32041
04213
40132

Ve birçok Sudoku oyuncusu bildiği gibi, kalan sayıları çıkarmak için tüm sayılara ihtiyacınız yok.

Buradaki zorluk Latin karesini mümkün olduğunca az bayta sıkıştırmak. Sıkıştıran / açan bir veya iki program sağlamanız gerekir.

Çeşitli bilgi:

  • Her zaman olacaktır kullanılan numaralar 0..N-1, Nköşeli kenarının uzunluğu veN<=25
  • Dekompresyonda latin kare girişle aynı olmalıdır.
  • Programınız yalnızca sağladığımları değil, herhangi bir latin kareyi (maksimum kare boyutunda) sıkıştırabilmelidir (de) . Sıkıştırma oranları da benzer olmalıdır.
  • Skorunuzu elde etmek için gerçekten sıkıştırma ve açma işlemlerini gerçekleştirmelisiniz (Evrenin sonu çalışma süreleri yok)

Test vakaları github'da bulunabilir . Puanınız toplam sıkıştırılmış test vakalarının büyüklüğüdür.

EDIT: 7 Temmuz 20: 07'den itibaren, bir test sorununu gidermek için test vakalarını güncelledim. Lütfen programınızı yeni test durumlarında tekrar çalıştırın. Teşekkürler Anders Kaseorg .


1
Eh, tanımı gereği, herhangi bir sembol kullanılır, ama benim test durumları sadece olabilir gerçekleşmesi kullanmak 0gerçi n-1:)
Nathan Merrill


3
@NathanMerrill iyi, noktaya sadece nfarklı semboller kullanma izni verildi . : P
Martin Ender

1
@DDCidC Boyutu bayt cinsinden ölçüldüğü için önemli olmamalıdır .
kusur,

2
Daki 25 test olguların 19 önemsiz Latin kare satır ve sütun permutasyonu ile üretilmiştir (4, 6, 8, 10, 12, 14 haricinde bütün olanlar) ( i , j ) giriştir i + j mod n . Bu, onları rastgele bir Latin karesinden çok daha fazla sıkıştırmak için çok kolaylaştırır. Kurallarınız tüm Latin kareler için benzer sıkıştırma oranlarına sahip olmamız gerektiğini söylese de, kazara kırılması kolay olabilir. Test durumları daha temsili olmalıdır.
Anders Kaseorg

Yanıtlar:


10

Python, 1281.375 1268.625 bayt

Her bir kararın bu üç formdan biri olduğu bir zamandaki Latin karesini bir “karar” olarak kodladık:

  • hangi sayı i satırında gider , j sütunu ;
  • i satırında , k sayısının girdiği sütun ;
  • j sütununda , k sayısı olan satırın içine girer.

Her adımda, önceki kararlara dayanarak yapabileceğimiz tüm mantıksal çıkarımları yaparız, sonra kararı mümkün olan en az sayıda seçenekle seçeriz, bu nedenle temsil etmek için en az sayıda bit alır.

Seçenekler basit bir aritmetik kod çözücü tarafından sağlanır (seçenek sayısına göre div / mod). Ama bu yapraklar kodlamada bazı fazlalık: eğer k seçimler tüm ürünün numaraları olan bir kareye şifresini çözer m , daha sonra k + m , k + 2⋅ m , k + 3⋅ m ... çözmek aynı kareye sonunda kalan bir durumla.

Karenin boyutunu açıkça kodlamaktan kaçınmak için bu fazlalıktan faydalanıyoruz. Dekompresör 1 büyüklüğündeki bir karenin kodunu çözmeye çalışarak başlar. Kod çözücü artık durumla bitince, bu sonucu ortaya çıkarır , orijinal sayıdan m çıkarır , boyutu 1 arttırır ve tekrar dener.

import numpy as np

class Latin(object):
    def __init__(self, size):
        self.size = size
        self.possible = np.full((size, size, size), True, dtype=bool)
        self.count = np.full((3, size, size), size, dtype=int)
        self.chosen = np.full((3, size, size), -1, dtype=int)

    def decision(self):
        axis, u, v = np.unravel_index(np.where(self.chosen == -1, self.count, self.size).argmin(), self.count.shape)
        if self.chosen[axis, u, v] == -1:
            ws, = np.rollaxis(self.possible, axis)[:, u, v].nonzero()
            return axis, u, v, list(ws)
        else:
            return None, None, None, None

    def choose(self, axis, u, v, w):
        t = [u, v]
        t[axis:axis] = [w]
        i, j, k = t
        assert self.possible[i, j, k]
        assert self.chosen[0, j, k] == self.chosen[1, i, k] == self.chosen[2, i, j] == -1

        self.count[1, :, k] -= self.possible[:, j, k]
        self.count[2, :, j] -= self.possible[:, j, k]
        self.count[0, :, k] -= self.possible[i, :, k]
        self.count[2, i, :] -= self.possible[i, :, k]
        self.count[0, j, :] -= self.possible[i, j, :]
        self.count[1, i, :] -= self.possible[i, j, :]
        self.count[0, j, k] = self.count[1, i, k] = self.count[2, i, j] = 1
        self.possible[i, j, :] = self.possible[i, :, k] = self.possible[:, j, k] = False
        self.possible[i, j, k] = True
        self.chosen[0, j, k] = i
        self.chosen[1, i, k] = j
        self.chosen[2, i, j] = k

def encode_sized(size, square):
    square = np.array(square, dtype=int)
    latin = Latin(size)
    chosen = np.array([np.argmax(square[:, :, np.newaxis] == np.arange(size)[np.newaxis, np.newaxis, :], axis=axis) for axis in range(3)])
    num, denom = 0, 1
    while True:
        axis, u, v, ws = latin.decision()
        if axis is None:
            break
        w = chosen[axis, u, v]
        num += ws.index(w)*denom
        denom *= len(ws)
        latin.choose(axis, u, v, w)
    return num

def decode_sized(size, num):
    latin = Latin(size)
    denom = 1
    while True:
        axis, u, v, ws = latin.decision()
        if axis is None:
            break
        if not ws:
            return None, 0
        latin.choose(axis, u, v, ws[num % len(ws)])
        num //= len(ws)
        denom *= len(ws)
    return latin.chosen[2].tolist(), denom

def compress(square):
    size = len(square)
    assert size > 0
    num = encode_sized(size, square)
    while size > 1:
        size -= 1
        square, denom = decode_sized(size, num)
        num += denom
    return '{:b}'.format(num + 1)[1:]

def decompress(bits):
    num = int('1' + bits, 2) - 1
    size = 1
    while True:
        square, denom = decode_sized(size, num)
        num -= denom
        if num < 0:
            return square
        size += 1

total = 0
with open('latin_squares.txt') as f:
    while True:
        square = [list(map(int, l.split(','))) for l in iter(lambda: next(f), '\n')]
        if not square:
            break

        bits = compress(square)
        assert set(bits) <= {'0', '1'}
        assert square == decompress(bits)
        print('Square {}: {} bits'.format(len(square), len(bits)))
        total += len(bits)

print('Total: {} bits = {} bytes'.format(total, total/8.0))

Çıktı:

Square 1: 0 bits
Square 2: 1 bits
Square 3: 3 bits
Square 4: 8 bits
Square 5: 12 bits
Square 6: 29 bits
Square 7: 43 bits
Square 8: 66 bits
Square 9: 94 bits
Square 10: 122 bits
Square 11: 153 bits
Square 12: 198 bits
Square 13: 250 bits
Square 14: 305 bits
Square 15: 363 bits
Square 16: 436 bits
Square 17: 506 bits
Square 18: 584 bits
Square 19: 674 bits
Square 20: 763 bits
Square 21: 877 bits
Square 22: 978 bits
Square 23: 1097 bits
Square 24: 1230 bits
Square 25: 1357 bits
Total: 10149 bits = 1268.625 bytes

Bu kodu Ideone'da deniyorum, ancak sadece çalışma zamanı hataları veriyor. F dosyası yerine stdin kullanarak değiştirdim. ideone.com/fKGSQd
edc65

@ edc65 Çalışmıyor çünkü Ideone NumPy eski.
Dennis,

@ edc65 İdeone, için çok eski olan NumPy 1.8.2'ye sahiptir np.stack(). Bu durumda, onunla değiştirilebiliyor np.array([…])ve ben bunu şu anki sürümünde yaptım.
Anders Kaseorg

hmmm. tüm kareler bir bayt akışında depolanır mı? boyutları hakkındaki bilgiler de saklanıyor mu, yoksa kod çözücü, 1,2,3, vb. boyutunda olduklarını varsayar mı?
Sarge Borsch

@SargeBorsch Her kare ayrı bir bit akışına sıkıştırılır. Dekompresör, tarif ettiğim algoritmayı kullanarak kare boyutunu bit akışından net bir şekilde kurtarır. Varsayım kullanılmaz.
Anders Kaseorg

7

MATLAB, 3'062.5 2'888.125 bayt

Bu yaklaşım sadece karenin son satırını ve son sütununu keser ve her girişi belirli bir bit derinliğine sahip sözcüklere dönüştürür. Bit derinliği, verilen boyut karesi için minimal olarak seçilir. (@KarlNapf tarafından yapılan öneri) Bu kelimeler birbirine eklenmiştir. Dekompresyon tam tersidir.

Tüm test durumları için toplam 23'105 bit veya 2'888.125 bayttır. (Çıktılarımın boyutu yalnızca girdinin boyutuna bağlı olduğundan, hala güncellenen test durumları için geçerlidir.)

function bin=compress(a)
%get rid of last row and column:
s=a(1:end-1,1:end-1);
s = s(:)';
bin = [];
%choose bit depth:
bitDepth = ceil(log2(numel(a(:,1))));
for x=s;
    bin = [bin, dec2bin(x,bitDepth)];
end
end

function a=decompress(bin)
%determine bit depth
N=0;
f=@(n)ceil(log2(n)).*(n-1).^2;
while f(N)~= numel(bin)
    N=N+1; 
end
bitDepth = ceil(log2(N));
%binary to decimal:
assert(mod(numel(bin),bitDepth)==0,'invalid input length')
a=[];
for k=1:numel(bin)/bitDepth;
    number = bin2dec([bin(bitDepth*(k-1) + (1:bitDepth)),' ']);
    a = [a,number];    
end
n = sqrt(numel(a));
a = reshape(a,n,n);
disp(a)
%reconstruct last row/column:
n=size(a,1)+1;
a(n,n)=0;%resize
%complete rows:
v = 0:n-1;
for k=1:n
    a(k,n) = setdiff(v,a(k,1:n-1));
    a(n,k) = setdiff(v,a(1:n-1,k));
end
end

Değişken bir bit oranı kullanarak biraz daha sıkıştırabilirsiniz, n=9..164 bit için yeterlidir.
Karl Napf

@KarlNapf Farklı uzunluktaki kelimeleri nasıl ayırt edersiniz? Bildiğim kadarıyla ek öneklere ihtiyacınız var, değil mi?
kusur,

Bir sıkıştırma içinde değişken değil, daha çok karenin boyutuna bağlı olarak. N> 16 ise sabit 5 bit kullanın, 8 <n <= 16 ise 4 bit sabit vb. Kullanın.
Karl Napf

Doğru, mantıklı, teşekkür ederim!
kusur,

3
Bunu aynı şekilde yapıyor olmanızdan dolayı, muhtemelen alışkın olduğunuz yoldur. =)
kusur,

7

Python 3, 10772 bit (1346,5 bayt)

def compress(rows):
    columns = list(zip(*rows))
    size = len(rows)
    symbols = range(size)
    output = size - 1
    weight = 25
    for i in symbols:
        for j in symbols:
            choices = set(rows[i][j:]) & set(columns[j][i:])
            output += weight * sorted(choices).index(rows[i][j])
            weight *= len(choices)
    return bin(output + 1)[3:]

def decompress(bitstring):
    number = int('1' + bitstring, 2) - 1
    number, size = divmod(number, 25)
    size += 1
    symbols = range(size)
    rows = [[None] * size for _ in symbols]
    columns = [list(column) for column in zip(*rows)]
    for i in symbols:
        for j in symbols:
            choices = set(symbols) - set(rows[i]) - set(columns[j])
            number, index = divmod(number, len(choices))
            rows[i][j] = columns[j][i] = sorted(choices)[index]
    return rows

Birleşik test durumlarını sıkıştırmak ve açmak için 0.1 saniye sürer.

İdeone'deki puanı doğrulayın .


Woah, açıklamak ister misin?
Nathan Merrill

1
Özetle, kompresör kare içinde okuma sırasına göre hareket eder, bu sırada ve sırada görünen sembollerin izini tutar ve sembolün indeksini olası sembollerin artan listesinde aritmetik olarak kodlar. Kodumu biraz temizledikten ve bijective base 256 herhangi bir bayt kaydedip kaydetmediğini test ettikten sonra ayrıntılı bir açıklama ekleyeceğim.
Dennis,

Kodunuzun ne yaptığından tam olarak emin değilim, ancak son satırı dışarıda bırakmak ve açma sırasında çözmek mümkün değil mi?
Yytsi

@TuukkaX tek olası sembol olmadığı zaman len(possible)ise 1 ve possible.index(rows[i][j])olduğu 0 sembolü hiçbir ücret ödemeden kodlanan böylece.
Dennis,

Yay, yeni test vakalarında 6 bit kaydetti. :)
Dennis,

3

J , 2444 bayt

A.Tamsayılı bir permütasyona [0, n) ve permütasyon endeksine dönüşmesi ve ondan dönüşmesi için yerleşik yapıya güvenir .

Sıkıştırma, 36 bayt

({:a.)joinstring<@(a.{~255&#.inv)@A.

Giriş, Latin karesini temsil eden 2d dizisidir. Her satır bir permütasyon indeksine dönüştürülür ve bu indeks bir baz 255 basamak listesine dönüştürülür ve bir ASCII değeri ile değiştirilir. Her dize 255'te ASCII karakteri kullanılarak birleştirilir.

Dekompres, 45 bayt

[:(A.i.@#)[:(_&,(255&#.&x:);._1~1,255&=)u:inv

Giriş dizesini her 255 ASCII değerinde böler ve her grubu 255 basamak olarak ayrıştırır. Daha sonra grup sayısını kullanarak, bir tamsayı listesi oluşturun (0, uzunluk) ve her dizine göre izin verin ve 2d dizisi olarak döndürün.


2

Python, 6052 4521 3556 bayt

compresskareyi çok örnekli bir dize olarak alır, tıpkı örnekler gibi ve ikili bir dize döndürür decompress, bunun tersini yapar.

import bz2
import math

def compress(L):
 if L=="0": 
  C = []
 else:
  #split elements
  elems=[l.split(',') for l in L.split('\n')]
  n=len(elems)
  #remove last row and col
  cropd=[e[:-1] for e in elems][:-1]
  C = [int(c) for d in cropd for c in d]

 #turn to string
 B=map(chr,C)
 B=''.join(B)

 #compress if needed
 if len(B) > 36:
  BZ=bz2.BZ2Compressor(9)
  BZ.compress(B)
  B=BZ.flush()

 return B

def decompress(C):

 #decompress if needed
 if len(C) > 40:
  BZ=bz2.BZ2Decompressor()
  C=BZ.decompress(C)

 #return to int and determine length
 C = map(ord,C)
 n = int(math.sqrt(len(C)))
 if n==0: return "0"

 #reshape to list of lists
 elems = [C[i:i+n] for i in xrange(0, len(C), n)]

 #determine target length
 n = len(elems[0])+1
 L = []
 #restore last column
 for i in xrange(n-1):
  S = {j for j in range(n)}
  L.append([])
  for e in elems[i]:
   L[i].append(e)
   S.remove(e)
  L[i].append(S.pop())
 #restore last row
 L.append([])
 for col in xrange(n):
  S = {j for j in range(n)}
  for row in xrange(n-1):
   S.remove(L[row][col])
  L[-1].append(S.pop())
 #merge elements
 orig='\n'.join([','.join([str(e) for e in l]) for l in L])
 return orig

Son satır + sütunu kaldırın ve gerisini sıkıştırın.

  • Edit1: iyi base64gerekli görünmüyor
  • Edit2: şimdi doğranmış tabloyu ikili bir dizgeye dönüştürüyor ve sadece gerekiyorsa sıkıştırıyor

2

Python 3, 1955 bayt

Yine permütasyon endekslerini kullanan bir tane daha ...

from math import factorial

test_data_name = 'latin_squares.txt'

def grid_reader(fname):
    ''' Read CSV number grids; grids are separated by empty lines '''
    grid = []
    with open(fname) as f:
        for line in f:
            line = line.strip()
            if line:
                grid.append([int(u) for u in line.split(',') if u])
            elif grid:
                yield grid
                grid = []
    if grid:
        yield grid

def show(grid):
    a = [','.join([str(u) for u in row]) for row in grid]
    print('\n'.join(a), end='\n\n')

def perm(seq, base, k):
    ''' Build kth ordered permutation of seq '''
    seq = seq[:]
    p = []
    for j in range(len(seq) - 1, 0, -1):
        q, k = divmod(k, base)
        p.append(seq.pop(q))
        base //= j
    p.append(seq[0])
    return p

def index(p):
    ''' Calculate index number of sequence p,
        which is a permutation of range(len(p))
    '''
    #Generate factorial base code
    fcode = [sum(u < v for u in p[i+1:]) for i, v in enumerate(p[:-1])]

    #Convert factorial base code to integer
    k, base = 0, 1
    for j, v in enumerate(reversed(fcode), 2):
        k += v * base
        base *= j
    return k

def encode_latin(grid):
    num = len(grid)
    fbase = factorial(num)

    #Encode grid rows by their permutation index,
    #in reverse order, starting from the 2nd-last row
    codenum = 0
    for row in grid[-2::-1]:
        codenum = codenum * fbase + index(row)
    return codenum

def decode_latin(num, codenum):
    seq = list(range(num))
    sbase = factorial(num - 1)
    fbase = sbase * num

    #Extract rows
    grid = []
    for i in range(num - 1):
        codenum, k = divmod(codenum, fbase)
        grid.append(perm(seq, sbase, k))

    #Build the last row from the missing element of each column
    allnums = set(seq)
    grid.append([allnums.difference(t).pop() for t in zip(*grid)])
    return grid

byteorder = 'little'

def compress(grid):
    num = len(grid)
    codenum = encode_latin(grid)
    length = -(-codenum.bit_length() // 8)
    numbytes = num.to_bytes(1, byteorder)
    codebytes = codenum.to_bytes(length, byteorder)
    return numbytes + codebytes

def decompress(codebytes):
    numbytes, codebytes= codebytes[:1], codebytes[1:]
    num = int.from_bytes(numbytes, byteorder)
    if num == 1:
        return [[0]]
    else:
        codenum = int.from_bytes(codebytes, byteorder)
        return decode_latin(num, codenum)

total = 0
for i, grid in enumerate(grid_reader(test_data_name), 1):
    #show(grid)
    codebytes = compress(grid)
    length = len(codebytes)
    total += length
    newgrid = decompress(codebytes)
    ok = newgrid == grid
    print('{:>2}: Length = {:>3}, {}'.format(i, length, ok))
    #print('Code:', codebytes)
    #show(newgrid)

print('Total bytes: {}'.format(total))

çıktı

 1: Length =   1, True
 2: Length =   1, True
 3: Length =   2, True
 4: Length =   3, True
 5: Length =   5, True
 6: Length =   7, True
 7: Length =  11, True
 8: Length =  14, True
 9: Length =  20, True
10: Length =  26, True
11: Length =  33, True
12: Length =  41, True
13: Length =  50, True
14: Length =  61, True
15: Length =  72, True
16: Length =  84, True
17: Length =  98, True
18: Length = 113, True
19: Length = 129, True
20: Length = 147, True
21: Length = 165, True
22: Length = 185, True
23: Length = 206, True
24: Length = 229, True
25: Length = 252, True
Total bytes: 1955

2

Python3 - 3572 3581 bayt

from itertools import *
from math import *

def int2base(x,b,alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
    if isinstance(x,complex):
        return (int2base(x.real,b,alphabet) , int2base(x.imag,b,alphabet))
    if x<=0:
        if x==0:return alphabet[0]
        else:return  '-' + int2base(-x,b,alphabet)
    rets=''
    while x>0:
        x,idx = divmod(x,b)
        rets = alphabet[idx] + rets
    return rets

def lexicographic_index(p):
    result = 0
    for j in range(len(p)):
        k = sum(1 for i in p[j + 1:] if i < p[j])
        result += k * factorial(len(p) - j - 1)
    return result

def getPermutationByindex(sequence, index):
    S = list(sequence)
    permutation = []
    while S != []:
        f = factorial(len(S) - 1)
        i = int(floor(index / f))
        x = S[i]
        index %= f
        permutation.append(x)
        del S[i]
    return tuple(permutation)

alphabet = "abcdefghijklmnopqrstuvwxyz"

def dataCompress(lst):
    n = len(lst[0])

    output = alphabet[n-1]+"|"

    for line in lst:
        output += "%s|" % int2base(lexicographic_index(line), 36)

    return output[:len(output) - 1]

def dataDeCompress(data):
    indexes = data.split("|")
    n = alphabet.index(indexes[0]) + 1
    del indexes[0]

    lst = []

    for index in indexes:
        if index != '':
            lst.append(getPermutationByindex(range(n), int(index, 36)))

    return lst

dataCompress tamsayı tuples listesini alır ve bir dize döndürür.

dateDeCompress bir dize alır ve tam sayı tuples listesini döndürür.

Kısacası, her bir program için, bu program bu satırların permütasyon endeksini alır ve bunu 36 tabanına kaydeder. Dekompresyon, büyük girdilerde uzun zaman alır ancak sıkıştırma, büyük girdilerde bile çok hızlıdır.

Kullanımı:

dataCompress([(2,0,1),(1,2,0),(0,1,2)])

sonuç: c|4|3|0

dataDeCompress("c|4|3|0")

sonuç: [(2, 0, 1), (1, 2, 0), (0, 1, 2)]


2
permutationsÇağrılarınızı listaramalara permutationssarmazsanız muhtemelen daha iyi bir çalışma süresi elde edersiniz - tüm permütasyonları tembel bir şekilde üreten bir jeneratör döndürür, ancak bunu bir hale getirmeye çalışırsanız list, hevesle alır ki tüm permütasyonları oluşturur, çok uzun zaman.
Mego

Kodunuzu nasıl kullanacağınızı biraz daha iyi açıklayabilir misiniz?
Mego

@Mego Tabi, belki de hala hesaplanmasa da tembel değerlendirmeyi de uygulayacağım.
Yytsi



1

Java, 2310 bayt

Karenin her satırını, faktoradik sayıları kullandığı ve aynı zamanda yüzdelik sayı sistemi için de bilinen bir faktoring sayı sistemi olarak kullandığı hangi sözlükbilimsel permütasyonu temsil eden bir sayıya dönüştürüyoruz .

Kareyi, ilk baytın karenin büyüklüğü olduğu bir ikilik dosyaya yazarız ve daha sonra her satırda bir Java BigInteger'ın ikili gösterimindeki bayt sayısı için bir bayt vardır, ardından bu BigInteger'ın baytları takip eder.

İşlemi tersine çevirmek ve kareyi açmak için, boyutu geri ve ardından her bir BigInteger okur ve karenin her satırını üretmek için bu sayıyı kullanırız.

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Latin {
    public static void main(String[] args) {
        if (args.length != 3) {
            System.out.println("java Latin {-c | -d} infile outfile");
        } else if (args[0].equals("-c")) {
            compress(args[1], args[2]);
        } else if (args[0].equals("-d")) {
            decompress(args[1], args[2]);
        } else {
            throw new IllegalArgumentException(
                "Invalid mode: " + args[0] + ", not -c or -d");
        }
    }

    public static void compress(String filename, String outname) {
        try (BufferedReader br = Files.newBufferedReader(Paths.get(filename))) {
            try (OutputStream os =
                    new BufferedOutputStream(new FileOutputStream(outname))) {
                String line = br.readLine();
                if (line == null) return;
                int size = line.split(",").length;
                if (size > 127) throw new ArithmeticException(
                    "Overflow: square too large");
                Permutor perm = new Permutor(size);
                os.write((byte) size); // write size of square

                do {
                    List<Integer> nums = Arrays.stream(line.split(","))
                        .map(Integer::new)
                        .collect(Collectors.toList());
                    byte[] bits = perm.which(nums).toByteArray();
                    os.write((byte) bits.length); // write length of bigint
                    os.write(bits); // write bits of bigint
                } while ((line = br.readLine()) != null);
            }
        } catch (IOException e) {
            System.out.println("Error compressing " + filename);
            e.printStackTrace();
        }
    }

    public static void decompress(String filename, String outname) {
        try (BufferedInputStream is =
                new BufferedInputStream(new FileInputStream(filename))) {
            try (BufferedWriter bw =
                    Files.newBufferedWriter(Paths.get(outname))) {
                int size = is.read(); // size of latin square
                Permutor perm = new Permutor(size);
                for (int i = 0; i < size; ++i) {
                    int num = is.read(); // number of bytes in bigint
                    if (num == -1) {
                        throw new IOException(
                            "Unexpected end of file reading " + filename);
                    }
                    byte[] buf = new byte[num];
                    int read = is.read(buf); // read bits of bigint into buf
                    if (read != num) {
                        throw new IOException(
                            "Unexpected end of file reading " + filename);
                    }
                    String row = perm.nth(new BigInteger(buf)).stream()
                        .map(Object::toString)
                        .collect(Collectors.joining(","));
                    bw.write(row);
                    bw.newLine();
                }
            }
        } catch (IOException e) {
            System.out.println("Error reading " + filename);
            e.printStackTrace();
        }
    }
}

Permutor, birkaç yıl önce permütasyonlarla çalışmak için yazdığım bir sınıftan uyarlandı:

import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.math.BigInteger;
import static java.math.BigInteger.ZERO;
import static java.math.BigInteger.ONE;

public class Permutor {
    private final List<Integer> items;

    public Permutor(int n) {
        items = new ArrayList<>();
        for (int i = 0; i < n; ++i) items.add(i);
    }

    public BigInteger size() {
        return factorial(items.size());
    }

    private BigInteger factorial(int x) {
        BigInteger f = ONE;
        for (int i = 2; i <= x; ++i) {
            f = f.multiply(BigInteger.valueOf(i));
        }
        return f;
    }

    public List<Integer> nth(long n) {
        return nth(BigInteger.valueOf(n));
    }

    public List<Integer> nth(BigInteger n) {
        if (n.compareTo(size()) > 0) {
            throw new IllegalArgumentException("too high");
        }
        n = n.subtract(ONE);
        List<Integer> perm = new ArrayList<>(items);
        int offset = 0, size = perm.size() - 1;
        while (n.compareTo(ZERO) > 0) {
            BigInteger fact = factorial(size);
            BigInteger mult = n.divide(fact);
            n = n.subtract(mult.multiply(fact));
            int pos = mult.intValue();
            Integer t = perm.get(offset + pos);
            perm.remove((int) (offset + pos));
            perm.add(offset, t);
            --size;
            ++offset;
        }
        return perm;
    }

    public BigInteger which(List<Integer> perm) {
        BigInteger n = ONE;
        List<Integer> copy = new ArrayList<>(items);
        int size = copy.size() - 1;
        for (Integer t : perm) {
            int pos = copy.indexOf(t);
            if (pos < 0) throw new IllegalArgumentException("invalid");
            n = n.add(factorial(size).multiply(BigInteger.valueOf(pos)));
            copy.remove((int) pos);
            --size;
        }
        return n;
    }
}

Kullanımı:

Latin karesi içeri girdiğinde latin.txtsıkıştırın:

java Latin -c latin.txt latin.compressed

Ve sıkıştırmasını aç:

java Latin -d latin.compressed latin.decompressed
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.