라틴 스퀘어 압축


31

라틴어 광장 아무 행이나 열에서 기호를 반복했다 정사각형이다 .

13420
21304
32041
04213
40132

많은 스도쿠 플레이어가 알고 있듯이 나머지 숫자를 추론하기 위해 모든 숫자가 필요하지는 않습니다.

당신의 도전은 가능한 한 적은 바이트로 라틴 스퀘어를 압축하는 것입니다. 압축 / 압축 해제하는 하나 또는 두 개의 프로그램을 제공해야합니다.

다양한 정보 :

  • 사용 된 숫자는 항상입니다 0..N-1. 여기 N에서 사각형 가장자리의 길이는 어디 입니까?N<=25
  • 압축 해제시 라틴 사각형은 입력과 동일해야합니다.
  • 귀하의 프로그램 은 내가 제공 한 것뿐만 아니라 모든 라틴 정사각형 (최대 정사각형 크기 내 )을 압축 해제 할 수 있어야합니다 . 압축비도 비슷해야합니다.
  • 실제로 점수를 얻으려면 압축 및 압축 해제기를 실행해야합니다 (유니버스 끝 런타임 없음)

테스트 케이스는 github 에서 찾을 수 있습니다 . 점수는 압축 된 테스트 사례의 총 크기입니다.

편집 : 7 월 7 일 20:07 현재 테스트 문제를 업데이트했습니다 (세대 문제를 해결하기 위해). 새로운 테스트 사례에서 프로그램을 다시 실행하십시오. 감사합니다 Anders Kaseorg .


1
음, 정의에 의해, 어떤 기호는 사용하지만, 내 테스트 케이스는 할 수있는 일이 사용 0하지만 n-1:)
나단 메릴


3
@NathanMerrill은 요점은 n다른 기호 만 사용할 수있었습니다 . : P
Martin Ender

1
@DavidC 크기는 바이트 단위 로 측정되므로 중요하지 않습니다 .
flawr

2
25 개의 테스트 사례 중 19 개 (4, 6, 8, 10, 12, 14를 제외한 모든 사례)는 ( i , j ) 항목이 i + j mod n 인 사소한 라틴 정사각형의 행과 열을 변경하여 생성되었습니다 . 이것은 임의의 라틴 정사각형보다 훨씬 쉽게 압축 할 수있게합니다. 규칙에 따라 모든 라틴 사각형에 대해 압축 비율이 비슷해야하지만 우연히 깨지기 쉽습니다. 테스트 사례가 더 대표적이어야합니다.
Anders Kaseorg

답변:


10

Python, 1281.375 1268.625 바이트

우리는 각 결정이 다음 세 가지 형식 중 하나 인 라틴 정사각형을 한 번에 하나의 "결정"으로 인코딩합니다.

  • i , 열 j 에있는 숫자 ;
  • i에서 , 숫자 k 가 들어가는 열 ;
  • 칼럼에서 J 숫자 열, K가 들어간다.

각 단계에서 이전 결정을 기반으로 할 수있는 모든 논리적 추론을 한 다음 가능한 적은 수의 선택을 통해 결정을 선택하므로 표시 할 비트 수가 가장 적습니다.

선택은 간단한 산술 디코더 (선택 수에 따라 div / mod)에 의해 제공됩니다. 그러나 잎 인코딩 일부 중복 : 만약 K의 선택의 모든 제품 번호이었던 사각으로 디코딩 m은 다음 케이 + m을 , K + 2⋅ m , K + 3⋅ m , ... 디코드 동일한 정방형 끝에 남은 상태가 있습니다.

이 중복성을 활용하여 정사각형의 크기를 명시 적으로 인코딩하지 않도록합니다. 압축 해제 기는 크기 1의 제곱을 해독하려고 시도합니다. 디코더가 남은 상태로 끝날 때마다 결과를 버리고 원래 숫자에서 m 을 빼고 크기를 1 씩 증가시킨 후 다시 시도합니다.

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

산출:

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

이 코드를 ideone에서 시도하고 있지만 런타임 오류가 발생합니다. 파일 f 대신 stdin을 사용하여 수정했습니다. ideone.com/fKGSQd
edc65

@ edc65 Ideone의 NumPy가 오래되어 작동하지 않습니다.
Dennis

@ edc65 Ideone에는 NumPy 1.8.2가 너무 오래되었습니다 np.stack(). 이 경우으로 대체 될 수 있으며 np.array([…])현재 버전에서 그렇게했습니다.
Anders Kaseorg

흠. 모든 정사각형이 1 바이트 스트림에 저장됩니까? 크기에 대한 정보도 저장되어 있습니까, 아니면 디코더는 크기가 1,2,3 등이라고 가정합니까?
Sarge Borsch

@SargeBorsch 각 사각형은 별도의 비트 스트림으로 압축됩니다. 압축 해제 기는 내가 설명한 알고리즘을 사용하여 비트 스트림에서 모호하지 않은 사각형 크기를 복구합니다. 가정은 사용되지 않습니다.
Anders Kaseorg

7

MATLAB, 3'062.5 2'888.125 바이트

이 접근 방식은 사각형의 마지막 행과 마지막 열을 분리하고 각 항목을 특정 비트 심도의 단어로 변환합니다. 주어진 크기 사각형에 대해 비트 심도가 최소로 선택됩니다. (@KarlNapf의 제안)이 단어들은 서로 추가됩니다. 감압은 그 반대입니다.

모든 테스트 사례의 합계는 23'105 비트 또는 2'888.125 바이트입니다. (출력 크기는 입력 크기에만 의존하므로 업데이트 된 테스트 사례는 계속 유지됩니다.)

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

가변 비트 전송률을 사용하여 조금 더 압축 할 수 있습니다 (예 : n=9..164 비트이면 충분).
Karl Napf

@KarlNapf 그러면 다른 길이의 단어를 어떻게 구별합니까? 내가 아는 한 추가 접두사가 필요하지 않습니까?
flawr

정사각형의 크기에 따라 하나의 압축 내에서 가변적이지 않습니다. n> 16이면 5 비트 고정을 사용하고 8 <n <= 16이면 4 비트 고정 등을 사용하십시오.
Karl Napf

아 맞아요. 감사합니다!
flawr

3
같은 이유로 당신이 다른 방식으로 그것을하고 있다면, 그것은 아마도 익숙한 방식 일 것입니다. =)
flawr

7

Python 3, 10772 비트 (1346.5 바이트)

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

결합 된 테스트 사례를 압축 및 압축 해제하는 데 0.1 초가 걸립니다.

Ideone 의 점수를 확인하십시오 .


와, 설명 할래?
Nathan Merrill

1
간단히 말해서, 압축기는 해당 행과 열에 이미 표시된 기호를 추적하고 가능한 기호의 오름차순 목록에서 기호의 색인을 산술적으로 인코딩하여 읽기 순서로 사각형을 통과합니다. 코드를 약간 정리하고 bijective base 256이 바이트를 저장하는지 테스트 한 후에 자세한 설명을 추가합니다.
Dennis

코드가 무엇을하고 있는지 확실하지 않지만 압축을 풀고 마지막 줄을 남겨두고 해결할 수 없습니까?
Yytsi

@TuukkaX 단지 하나의 가능한 기호가있을 때 len(possible)이다 (1)possible.index(rows[i][j])0 심볼 무료로 인코딩되도록.
Dennis

예, 새로운 테스트 사례는 6 비트를 절약했습니다. :)
Dennis

3

J , 2444 바이트

A.정수 [0, n)의 순열과 순열 인덱스를 변환하기 위해 내장 을 사용합니다 .

압축, 36 바이트

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

입력은 라틴 정사각형을 나타내는 2D 배열입니다. 각 행은 순열 색인으로 변환되고 해당 색인은 기본 255 자리 목록으로 변환되어 ASCII 값으로 대체됩니다. 그런 다음 각 문자열은 255에서 ASCII 문자를 사용하여 결합됩니다.

압축 해제, 45 바이트

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

입력 문자열을 각 ASCII 값 255로 나누고 각 그룹을 기본 255 자리로 구문 분석합니다. 그런 다음 그룹 수를 사용하여 정수 [0, 길이) 목록을 작성하고 각 색인에 따라 정수를 작성하고 2D 배열로 리턴하십시오.


2

Python, 6052 4521 3556 바이트

compress예제와 마찬가지로 정사각형을 여러 줄 문자열로 사용하고 이진 문자열을 반환하는 반면 decompress그 반대의 경우도 마찬가지 입니다.

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

마지막 행 + 열을 제거하고 나머지는 압축하십시오.

  • Edit1 : base64필요하지 않은 것 같습니다
  • Edit2 : 이제 잘린 테이블을 이진 문자열로 변환하고 필요한 경우에만 압축

2

파이썬 3, 1955 바이트

그러나 순열 인덱스를 사용하는 또 다른 하나는 ...

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

산출

 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 바이트

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 정수 튜플 목록을 가져 와서 문자열을 반환합니다.

dateDeCompress 문자열을 가져와 정수 튜플 목록을 반환합니다.

즉, 각 줄에 대해이 프로그램은 해당 줄 순열 색인을 가져 와서 밑줄 36에 저장합니다. 큰 입력에서는 압축 해제에 시간이 오래 걸리지 만 큰 입력에서도 압축이 빠릅니다.

용법:

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

결과: c|4|3|0

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

결과: [(2, 0, 1), (1, 2, 0), (0, 1, 2)]


2
permutations호출에서 list호출을 래핑하지 않으면 런타임이 훨씬 더 좋아질 것입니다 - permutations생성기를 반환하여 모든 순열을 느리게 생성하지만,로 변경하려고하면 list모든 순열을 열심히 생성합니다. 아주 오랜 시간.
Mego

코드 사용법을 좀 더 잘 설명해 주시겠습니까?
Mego

@Mego Sure, 아마도 아직 계산할 수는 없지만 게으른 평가도 구현할 것입니다.
Yytsi



1

자바, 2310 바이트

정사각형의 각 행은 팩토리얼 번호 시스템 이라고도하는 팩토리얼 숫자를 사용하는 어휘 순열을 나타내는 숫자로 변환하여 순열의 번호를 매기는 데 유용합니다.

첫 번째 바이트가 사각형의 크기 인 이진 파일에 사각형을 쓴 다음 각 행에는 Java BigInteger의 이진 표현에서 바이트 수에 대해 1 바이트가 있고 그 뒤에 BigInteger의 바이트가옵니다.

프로세스를 되돌리고 사각형을 압축 해제하려면 크기를 다시 읽은 다음 각 BigInteger를 읽고 해당 숫자를 사용하여 사각형의 각 행을 생성하십시오.

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

순열은 몇 년 전에 순열을 다루기 위해 쓴 수업에서 채택되었습니다.

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

용법:

에서 라틴 사각형을 사용하여 latin.txt압축하십시오.

java Latin -c latin.txt latin.compressed

그리고 압축을 푸십시오 :

java Latin -d latin.compressed latin.decompressed
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.