Compressione quadrata latina


31

Un quadrato latino è una piazza che non ha ripetuto simboli nelle righe o colonne: .

13420
21304
32041
04213
40132

E come molti giocatori di Sudoku sanno, non hai bisogno di tutti i numeri per dedurre i numeri rimanenti.

La tua sfida è comprimere un quadrato latino nel minor numero di byte possibile. Devi fornire uno o due programmi che comprimono / decomprimono.

Informazioni varie:

  • I numeri utilizzati saranno sempre 0..N-1, dov'è Nla lunghezza del bordo del quadrato eN<=25
  • In fase di decompressione, il quadrato latino deve essere identico all'input.
  • I tuoi programmi dovrebbero essere in grado di (de) comprimere qualsiasi quadrato latino (entro la dimensione massima quadrata), non solo quelli che ho fornito. Anche i rapporti di compressione dovrebbero essere simili.
  • Devi effettivamente eseguire la compressione e il decompressore per ottenere il tuo punteggio (Nessun runtime di fine dell'universo)

I casi di test sono disponibili su github . Il tuo punteggio è la dimensione totale dei casi di test compressi.

EDIT: a partire dalle 20:07 del 7 luglio, ho aggiornato i casi di test (al fine di risolvere un problema di generazione). Rieseguire il programma sui nuovi casi di test. Grazie Anders Kaseorg .


1
Beh, per definizione, qualsiasi simbolo potrebbe essere utilizzato, ma i miei casi di test appena capita di usare 0però n-1:)
Nathan Merrill


3
@NathanMerrill, al punto era permesso solo usare nsimboli diversi. : P
Martin Ender,

1
@DavidC Non dovrebbe importare, poiché la dimensione è misurata in byte .
Flawr,

2
19 dei tuoi 25 casi di test (tutti quelli esclusi 4, 6, 8, 10, 12, 14) sono stati generati permutando le righe e le colonne del banale quadrato latino la cui voce ( i , j ) è i + j mod n . Questo li rende molto facili da comprimere molto più di un quadrato latino casuale. Sebbene le tue regole affermino che dovremmo avere rapporti di compressione simili per tutti i quadrati latini, questo potrebbe essere facile da infrangere per caso. I casi di test dovrebbero essere più rappresentativi.
Anders Kaseorg,

Risposte:


10

Python, 1281.375 1268.625 byte

Codifichiamo la piazza latina una "decisione" alla volta, dove ogni decisione è di una di queste tre forme:

  • quale numero va nella riga i , colonna j ;
  • nella riga i , in quale colonna compare il numero k ;
  • nella colonna j , in quale riga compare il numero k .

Ad ogni passo, facciamo tutte le inferenze logiche che possiamo basarci su decisioni precedenti, quindi prendiamo la decisione con il minor numero di scelte possibili, che quindi richiedono il minor numero di bit per rappresentare.

Le scelte sono fornite da un semplice decodificatore aritmetico (div / mod per il numero di scelte). Ma questo lascia un po 'di ridondanza nella codifica: se k decodifica in un quadrato in cui il prodotto di tutti i numeri delle scelte era m , allora k + m , k + 2⋅ m , k + 3⋅ m , ... decodifica nello stesso quadrato con qualche stato residuo alla fine.

Approfittiamo di questa ridondanza per evitare di codificare esplicitamente la dimensione del quadrato. Il decompressore inizia cercando di decodificare un quadrato di dimensione 1. Ogni volta che il decodificatore termina con lo stato rimanente, elimina quel risultato, sottrae m dal numero originale, aumenta la dimensione di 1 e riprova.

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))

Produzione:

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

Sto provando questo codice su ideone, ma dà solo errori di runtime. L'ho modificato usando stdin invece del file f. ideone.com/fKGSQd
edc65

@ edc65 Non funziona perché NumPy di ​​Ideone è obsoleto.
Dennis,

@ edc65 Ideone ha NumPy 1.8.2 che è troppo vecchio per np.stack(). In questo caso può essere sostituito con np.array([…]), e l'ho fatto nella versione corrente.
Anders Kaseorg,

hmmm. tutti i quadrati sono memorizzati in un flusso di byte? vengono memorizzate anche le informazioni sulla loro dimensione o il decodificatore presuppone che siano della dimensione 1,2,3, ... ecc.?
Sarge Borsch,

@SargeBorsch Ogni quadrato è compresso in un flusso di bit separato. Il decompressore recupera le dimensioni quadrate in modo inequivocabile dal flusso di bit, utilizzando l'algoritmo che ho descritto. Non viene utilizzato alcun presupposto.
Anders Kaseorg,

7

MATLAB, 3'062.5 2'888.125 byte

Questo approccio elimina solo l'ultima riga e l'ultima colonna del quadrato e converte ogni voce in parole di una certa profondità di bit. La profondità di bit è scelta minima per il quadrato di dimensioni indicate. (Suggerimento di @KarlNapf) Queste parole sono appena aggiunte l'una all'altra. La decompressione è solo il contrario.

La somma per tutti i casi di test è 23'105 bit o 2'888.125 byte. (Vale ancora per i casi di test aggiornati, poiché la dimensione dei miei output dipende solo dalla dimensione dell'input.)

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

Puoi comprimere un po 'di più usando un bitrate variabile, come per n=9..164 bit sono sufficienti.
Karl Napf,

@KarlNapf Come fai a discriminare le parole di diversa lunghezza? Per quanto ne so, allora hai bisogno di prefissi aggiuntivi, vero?
Flawr,

Non variabile all'interno di una compressione, più simile a seconda della dimensione del quadrato. Se n> 16 usa 5 bit fissi, se 8 <n <= 16 usa 4 bit fissi e così via.
Karl Napf,

Oh giusto questo ha senso, grazie!
Flawr,

3
Per lo stesso motivo per cui lo fai al contrario, è probabilmente il modo in cui sei abituato. =)
flawr

7

Python 3, 10772 bit (1346,5 byte)

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

Impiega 0,1 secondi per comprimere e decomprimere i casi di test combinati.

Verifica il punteggio su Ideone .


Woah, ti interessa spiegare?
Nathan Merrill,

1
In poche parole, il compressore viaggia attraverso il quadrato in ordine di lettura, tenendo traccia dei simboli già presenti in quella riga e colonna e codificando aritmeticamente l'indice del simbolo nell'elenco crescente di possibili simboli. Aggiungerò una spiegazione dettagliata dopo aver ripulito il mio codice e verificato se la base biiettiva 256 salva qualche byte.
Dennis,

Non sono completamente sicuro di cosa stia facendo il tuo codice, ma non è possibile lasciare fuori l'ultima riga e risolverlo durante la decompressione?
Yytsi,

@TuukkaX Quando c'è un solo simbolo possibile len(possible)è 1 ed possible.index(rows[i][j])è 0 , quindi quel simbolo viene codificato gratuitamente.
Dennis,

Sì, i nuovi casi di test hanno salvato 6 bit. :)
Dennis,

3

J , 2444 byte

Si basa sull'integrato A.per la conversione da e verso una permutazione di numeri interi [0, n) e un indice di permutazione.

Comprimi, 36 byte

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

L'input è un array 2d che rappresenta il quadrato latino. Ogni riga viene convertita in un indice di permutazione e tale indice viene convertito in un elenco di 255 cifre di base e sostituito con un valore ASCII. Ogni stringa viene quindi unita usando il carattere ASCII in 255.

Decomprimere, 45 byte

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

Divide la stringa di input per ciascun valore ASCII di 255 e analizza ciascun gruppo come 255 cifre di base. Quindi utilizzando il numero di gruppi, creare un elenco di numeri interi [0, lunghezza) e permutarlo in base a ciascun indice e restituirlo come un array 2d.


2

Python, 6052 4521 3556 byte

compressprende il quadrato come una stringa multilinea, proprio come gli esempi e restituisce una stringa binaria, mentre decompressfa il contrario.

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

Rimuovi l'ultima riga + colonna e comprimi il resto.

  • Edit1: bene base64non sembra necessario
  • Edit2: ora converte la tabella troncata in una stringa binaria e comprime solo se necessario

2

Python 3, 1955 byte

Ancora un altro che utilizza indici di permutazione ...

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))

produzione

 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 - 3.572 3.581 byte

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 prende un elenco di tuple intere e restituisce una stringa.

dateDeCompress accetta una stringa e restituisce un elenco di tuple intere.

In breve, per ogni linea, questo programma prende quell'indice di permutazione delle linee e lo salva nella base 36. La decompressione richiede molto tempo con input grandi ma la compressione è davvero veloce anche su input grandi.

Uso:

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

risultato: c|4|3|0

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

risultato: [(2, 0, 1), (1, 2, 0), (0, 1, 2)]


2
Probabilmente otterresti un runtime molto migliore se non avvolgessi le permutationschiamate in listchiamate - permutationsrestituisce un generatore, che genera pigramente tutte le permutazioni, ma se provi a trasformarlo in un list, genera avidamente tutte le permutazioni, il che richiede un tempo molto lungo.
Mego

Potresti spiegare un po 'meglio come usare il tuo codice?
Mego

@Mego Certo, forse implementerò anche la valutazione pigra, anche se è ancora abbastanza incontestabile.
Yytsi,



1

Java, 2310 byte

Convertiamo ogni riga del quadrato in un numero che rappresenta quale permutazione lessicografica sta usando numeri fattadici, noto anche come sistema numerico fattoriale , utile per numerare le permutazioni.

Scriviamo il quadrato in un file binario in cui il primo byte ha le dimensioni del quadrato, quindi ogni riga ha un byte per il numero di byte nella rappresentazione binaria di un BigInteger Java, seguito dai byte di quel BigInteger.

Per invertire il processo e decomprimere il quadrato leggiamo la dimensione indietro e quindi ogni BigInteger e usiamo quel numero per generare ogni riga del quadrato.

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 è adattato da una classe che ho scritto qualche anno fa per lavorare con le permutazioni:

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

Uso:

Con un quadrato latino in latin.txt, comprimilo:

java Latin -c latin.txt latin.compressed

E decomprimilo:

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