Nén vuông Latin


31

Hình vuông Latin là hình vuông không có ký hiệu lặp lại trong các hàng hoặc cột : .

13420
21304
32041
04213
40132

Và như nhiều người chơi Sudoku biết, bạn không cần tất cả các số để suy ra các số còn lại.

Thách thức của bạn là nén một hình vuông Latin thành càng ít byte càng tốt. Bạn cần cung cấp một hoặc hai chương trình nén / giải nén.

Thông tin khác nhau:

  • Các số được sử dụng sẽ luôn là 0..N-1, Nđộ dài của cạnh hình vuông vàN<=25
  • Khi giải nén, hình vuông Latin phải giống hệt với đầu vào.
  • (Các) chương trình của bạn sẽ có thể (de) nén bất kỳ hình vuông Latin nào (trong kích thước hình vuông tối đa), không chỉ những hình tôi đã cung cấp. Tỷ lệ nén cũng tương tự.
  • Bạn thực sự phải chạy bộ nén và giải nén để có được điểm số của mình (Không có thời gian chạy cuối vũ trụ)

Các trường hợp thử nghiệm có thể được tìm thấy trên github . Điểm của bạn là tổng kích thước của các trường hợp kiểm tra nén.

EDIT: Kể từ ngày 20:07 ngày 7 tháng 7, tôi đã cập nhật các trường hợp thử nghiệm (để khắc phục sự cố thế hệ). Vui lòng chạy lại chương trình của bạn trên các trường hợp thử nghiệm mới. Cảm ơn anh Kaseorg .


1
Vâng, theo định nghĩa, bất kỳ biểu tượng có thể được sử dụng, nhưng trường hợp thử nghiệm của tôi chỉ xảy ra để sử dụng 0mặc dù n-1:)
Nathan Merrill


3
@NathanMerrill tốt, điểm này chỉ được phép sử dụng ncác biểu tượng khác nhau. : P
Martin Ender

1
@DavidC Không thành vấn đề, vì kích thước được đo bằng byte .
flawr

2
19 trong số 25 trường hợp thử nghiệm của bạn (tất cả những trường hợp ngoại trừ 4, 6, 8, 10, 12, 14) đã được tạo bằng cách hoán vị các hàng và cột của hình vuông Latin tầm thường có mục nhập ( i , j ) là i + j mod n . Điều này làm cho chúng rất dễ nén hơn nhiều so với hình vuông Latin ngẫu nhiên. Mặc dù các quy tắc của bạn nói rằng chúng ta nên có tỷ lệ nén tương tự cho tất cả các ô vuông Latin, nhưng điều này có thể dễ dàng bị phá vỡ do tai nạn. Các trường hợp thử nghiệm nên được đại diện hơn.
Anders Kaseorg

Câu trả lời:


10

Python, 1281.375 1268.625 byte

Chúng tôi mã hóa một hình vuông Latin một quyết định, một lúc, trong đó mỗi quyết định là một trong ba hình thức sau:

  • số nào đi theo hàng i , cột j ;
  • trong hàng i , cột nào số k đi vào;
  • trong cột j , hàng nào số k đi vào.

Ở mỗi bước, chúng tôi thực hiện tất cả các suy luận logic mà chúng tôi có thể dựa trên các quyết định trước đó, sau đó chọn quyết định với số lượng lựa chọn nhỏ nhất có thể, do đó lấy số bit nhỏ nhất để biểu diễn.

Các lựa chọn được cung cấp bởi một bộ giải mã số học đơn giản (div / mod theo số lượng lựa chọn). Nhưng điều đó để lại một số dư thừa trong mã hóa: nếu k giải mã thành một hình vuông trong đó tích của tất cả các số lựa chọn là m , thì k + m , k + 2⋅ m , k + 3⋅ m , cách giải mã thành cùng một hình vuông với một số trạng thái còn sót lại ở cuối.

Chúng tôi tận dụng sự dư thừa này để tránh mã hóa rõ ràng kích thước của hình vuông. Bộ giải nén bắt đầu bằng cách cố gắng giải mã một hình vuông có kích thước 1. Bất cứ khi nào bộ giải mã kết thúc với trạng thái còn sót lại, nó sẽ đưa ra kết quả đó, trừ m khỏi số ban đầu, tăng kích thước lên 1 và thử lại.

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

Đầu ra:

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

Tôi đang thử mã này ở ideone, nhưng nó chỉ báo lỗi thời gian chạy. Tôi đã sửa đổi nó bằng cách sử dụng stdin thay vì tập tin f. ideone.com/fKGSQd
edc65

@ edc65 Nó không hoạt động vì NumPy của Ideone đã lỗi thời.
Dennis

@ edc65 Ideone có NumPy 1.8.2 quá cũ np.stack(). Trong trường hợp này, nó có thể được thay thế bằng np.array([…]), và tôi đã làm như vậy trong phiên bản hiện tại.
Anders Kaseorg

hừm tất cả các hình vuông được lưu trữ trong một luồng byte? thông tin về kích thước của chúng cũng được lưu trữ hay bộ giải mã giả định rằng chúng có kích thước 1,2,3, có phải không?
Sange Borsch

@SargeBorsch Mỗi ô vuông được nén thành một dòng bit riêng. Bộ giải nén phục hồi kích thước hình vuông một cách rõ ràng từ luồng bit, sử dụng thuật toán mà tôi đã mô tả. Không có giả định được sử dụng.
Anders Kaseorg

7

MATLAB, 3'062,5 2'888.125 byte

Cách tiếp cận này chỉ bỏ qua hàng cuối cùng và cột cuối cùng của hình vuông, và chuyển đổi từng mục thành các từ có độ sâu bit nhất định. Độ sâu bit được chọn tối thiểu cho hình vuông kích thước nhất định. (Gợi ý của @KarlNapf) Những từ này chỉ được thêm vào nhau. Giải nén chỉ là đảo ngược.

Tổng cho tất cả các trường hợp thử nghiệm là 23'105 bit hoặc 2'888.125 byte. (Vẫn giữ cho các trường hợp kiểm tra được cập nhật, vì kích thước đầu ra của tôi chỉ phụ thuộc vào kích thước của đầu vào.)

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

Bạn có thể nén thêm một chút bằng cách sử dụng bitrate biến đổi, như với n=9..164 bit là đủ.
Karl Napf

@KarlNapf Làm thế nào để bạn phân biệt các từ có độ dài khác nhau sau đó? Theo như tôi biết thì bạn cần thêm tiền tố, phải không?
flawr

Không thay đổi bên trong một lần nén, giống như tùy thuộc vào kích thước của hình vuông. Nếu n> 16 thì sử dụng 5 bit cố định, nếu 8 <n <= 16 sử dụng 4 bit cố định và cứ thế.
Karl Napf

Oh phải điều này có ý nghĩa, cảm ơn bạn!
flawr

3
Vì lý do tương tự mà bạn đang làm theo cách khác, đó có thể là cách bạn đã từng sử dụng. =)
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

Mất 0,1 giây để nén và giải nén các trường hợp thử nghiệm kết hợp.

Xác nhận điểm số trên Ideone .


Ái chà, quan tâm giải thích?
Nathan Merrill

1
Tóm lại, máy nén đi qua ô vuông theo thứ tự đọc, theo dõi các ký hiệu đã xuất hiện trong hàng và cột đó và mã hóa một cách hợp lý chỉ số của ký hiệu trong danh sách tăng dần của các ký hiệu có thể. Tôi sẽ thêm một lời giải thích chi tiết sau khi làm sạch mã của mình một chút và kiểm tra xem cơ sở tính toán 256 có lưu bất kỳ byte nào không.
Dennis

Tôi không chắc chắn mã của bạn đang làm gì, nhưng không thể bỏ dòng cuối cùng ra và giải quyết nó trong khi giải nén?
Yytsi

@TuukkaX Khi chỉ có một biểu tượng có thể len(possible)1possible.index(rows[i][j])0 , do đó biểu tượng được mã hóa hoàn toàn miễn phí.
Dennis

Yay, các trường hợp thử nghiệm mới lưu 6 bit. :)
Dennis

3

J , 2444 byte

Dựa vào nội dung A.để chuyển đổi sang và từ một hoán vị số nguyên [0, n) và chỉ số hoán vị.

Nén, 36 byte

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

Đầu vào là một mảng 2d đại diện cho hình vuông Latin. Mỗi hàng được chuyển đổi thành một chỉ mục hoán vị và chỉ mục đó được chuyển đổi thành một danh sách gồm 255 chữ số cơ bản và được thay thế bằng giá trị ASCII. Mỗi chuỗi sau đó được nối bằng ký tự ASCII ở 255.

Giải nén, 45 byte

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

Chia chuỗi đầu vào ở mỗi giá trị ASCII là 255 và phân tích từng nhóm làm 255 chữ số cơ bản. Sau đó, sử dụng số lượng nhóm, tạo một danh sách các số nguyên [0, độ dài) và hoán vị nó theo từng chỉ số và trả về dưới dạng mảng 2d.


2

Python, 6052 4521 3556 byte

compresslấy hình vuông là một chuỗi nhiều dòng, giống như các ví dụ và trả về một chuỗi nhị phân, trong khi decompressngược lại.

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

Xóa hàng cuối cùng + cột và nén phần còn lại.

  • Edit1: base64có vẻ không cần thiết
  • Edit2: bây giờ chuyển đổi bảng được cắt thành chuỗi nhị phân và chỉ nén nếu cần thiết

2

Python 3, 1955 byte

Một số khác sử dụng các chỉ số hoán vị ...

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

đầu ra

 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 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 lấy một danh sách các bộ số nguyên và trả về một chuỗi.

dateDeCompress lấy một chuỗi và trả về một danh sách các bộ số nguyên.

Nói tóm lại, đối với mỗi dòng, chương trình này lấy chỉ số hoán vị dòng đó và lưu nó trong cơ sở 36. Việc giải nén mất nhiều thời gian với đầu vào lớn nhưng nén thực sự nhanh ngay cả trên đầu vào lớn.

Sử dụng:

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

kết quả: c|4|3|0

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

kết quả: [(2, 0, 1), (1, 2, 0), (0, 1, 2)]


2
Bạn có thể sẽ có được thời gian chạy tốt hơn rất nhiều nếu bạn không thực hiện các permutationscuộc gọi của mình trong listcác cuộc gọi - permutationstrả về một trình tạo, nó tạo ra tất cả các hoán vị một cách lười biếng, nhưng nếu bạn cố gắng thực hiện nó list, nó sẽ háo hức tạo ra tất cả các hoán vị, sẽ mất một thời gian rất dài.
Mego

Bạn có thể giải thích tốt hơn một chút về cách sử dụng mã của bạn?
Mego

@Mego Chắc chắn, có lẽ tôi cũng sẽ thực hiện đánh giá lười biếng, mặc dù nó vẫn không thể thực hiện được.
Yytsi



1

Java, 2310 byte

Chúng tôi chuyển đổi từng hàng của hình vuông thành một số đại diện cho phép hoán vị từ vựng mà nó đang sử dụng các số nhân tố, còn được gọi là hệ thống số giai thừa , rất hữu ích cho việc hoán vị đánh số.

Chúng ta viết hình vuông vào một tệp nhị phân trong đó byte đầu tiên là kích thước của hình vuông và sau đó mỗi hàng có một byte cho số byte trong biểu diễn nhị phân của BigInteger Java, theo sau là byte của BigInteger.

Để đảo ngược quá trình và giải nén hình vuông, chúng ta đọc lại kích thước và sau đó mỗi BigInteger và sử dụng số đó để tạo từng hàng của hình vuông.

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 được điều chỉnh từ một lớp tôi đã viết vài năm trước để làm việc với hoán vị:

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

Sử dụng:

Với một hình vuông Latin latin.txt, nén nó:

java Latin -c latin.txt latin.compressed

Và giải nén nó:

java Latin -d latin.compressed latin.decompressed
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.