ラテン方陣圧縮


31

ラテン方陣は、行または列には繰り返しのシンボルを持っていない正方形であります:

13420
21304
32041
04213
40132

そして、多くの数独プレイヤーが知っているように、残りの数字を推測するためにすべての数字が必要なわけではありません。

あなたの課題は、ラテン方陣をできるだけ少ないバイトに圧縮することです。 圧縮/解凍する1つまたは2つのプログラムを提供する必要があります。

さまざまな情報:

  • 使用される数値は常にになります0..N-1。ここNで、は正方形のエッジの長さです。N<=25
  • 解凍時には、ラテン方格は入力と同一でなければなりません。
  • あなたのプログラム(複数可)(デ)圧縮することができる必要があります任意の(最大の正方形サイズ以内)ラテン方陣、私が提供してきたものだけでなく。圧縮率も同様である必要があります。
  • 実際に圧縮および解凍プログラムを実行してスコアを取得する必要があります(エンドオブユニバースランタイムはありません)

テストケースはgithubにあります。 スコアは、圧縮されたテストケースの合計サイズです。

編集:7月7日20:07の時点で、テストケースを更新しました(生成の問題を修正するため)。新しいテストケースでプログラムを再実行してください。Anders Kaseorgに 感謝します。


1
まあ、定義により、任意のシンボルを使用できますが、私のテストケースはたまたま使用し0ますn-1:)
ネイサンメリル


3
@NathanMerrillよく、ポイントはn異なるシンボルを使用することだけが許可されていました。:P
マーティンエンダー

1
@DavidCサイズはバイト単位で測定されるため、問題ではありません。
-flawr

2
25個のテストケースのうち19個(4、6、8、10、12、14を除くすべて)は、(ij)エントリがi + j mod nである単純なラテン方格の行と列を並べ替えることによって生成されました。これにより、ランダムなラテン方陣よりもはるかに簡単に圧縮できます。すべてのラテン方格について同様の圧縮率を使用する必要があるとのルールがありますが、これは偶然に簡単に破られる可能性があります。テストケースはより代表的なものにする必要があります。
アンデルスカセオルグ16

回答:


10

Python、1281.375 1268.625バイト

ラテン方格を一度に1つの「決定」でエンコードします。各決定は、次の3つの形式のいずれかです。

  • i、列jの番号。
  • i、番号kが入る列。
  • jで、番号kが入る行。

各ステップで、以前の決定に基づいて可能な論理的推論をすべて行い、可能な限り少ない数の選択で決定を選択します。したがって、表現するビットの数が最も少なくなります。

選択肢は、単純な算術デコーダー(選択肢の数によるdiv / mod)によって提供されます。しかし、その葉符号化におけるいくつかの冗長性:場合にk個の選択肢の全ての製品番号があった正方形のデコードmは、次いでkは + MはK +2⋅ MK +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は古いため機能しません。
デニス

@ edc65 IdeoneにはNumPy 1.8.2があり、これはには古すぎnp.stack()ます。この場合、に置き換えることができますnp.array([…])。現在のバージョンではこれを行っています。
アンデルスカセオルグ16

うーん。すべての正方形が1バイトストリームに格納されていますか?サイズに関する情報も保存されますか、またはデコーダーはサイズが1,2,3などであると想定しますか?
セージボルシュ

@SargeBorsch各正方形は、個別のビットストリームに圧縮されます。圧縮解除プログラムは、説明したアルゴリズムを使用して、ビットストリームから正方形のサイズを明確に復元します。仮定は使用されません。
アンデルスカセオルグ

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ビットで十分であるなど、もう少し圧縮できます。
カールナップ16

@KarlNapfでは、長さの異なる単語をどのように区別しますか?私の知る限り、追加のプレフィックスが必要ですよね?
-flawr

1つの圧縮内で可変ではなく、正方形のサイズに依存します。n> 16の場合は5ビット固定を使用し、8 <n <= 16の場合は4ビット固定を使用します。
カールナップ16

ああ、これは理にかなっています、ありがとう!
-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のスコアを確認します。


うわー、説明する気?
ネイサンメリル

1
一言で言えば、コンプレッサーは正方形を読み上げ順に移動し、その行と列に既に現れている記号を追跡し、可能な記号の昇順リストの記号のインデックスを算術的にエンコードします。コードを少しクリーンアップし、全単射256がバイトを節約できるかどうかをテストした後、詳細な説明を追加します。
デニス

あなたのコードが何をしているのか完全にはわかりませんが、最後の行を残して、解凍中に解決することはできませんか?
Yytsi

@TuukkaX唯一の可能なシンボルがあるとlen(possible)され1possible.index(rows[i][j])なる0シンボルが無償で符号化されるように、。
デニス

そう、新しいテストケースでは6ビット節約できました。:)
デニス

3

J、2444バイト

A.整数[0、n)の順列と順列インデックスとの間の変換を、組み込み関数に依存しています。

圧縮、36バイト

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

入力は、ラテン方陣を表す2次元配列です。各行は置換インデックスに変換され、そのインデックスはベース255桁のリストに変換され、ASCII値に置き換えられます。各文字列は、255のASCII文字を使用して結合されます。

解凍、45バイト

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

255の各ASCII値で入力文字列を分割し、各グループを255桁で解析します。次に、グループの数を使用して、整数[0、長さ)のリストを作成し、各インデックスに従って並べ替えて、2次元配列として返します。


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

最後の行と列を削除し、残りを圧縮します。

  • 編集1:まあbase64必要はないようです
  • Edit2:切り刻まれたテーブルをバイナリ文字列に変換し、必要な場合にのみ圧縮するようになりました

2

Python 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確かに、それはまだかなり計算不可能ですが、遅延評価も実装するでしょう。
Yytsi



1

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

Permutorは、数年前に書いたクラスを順列で動作するように適合させたものです。

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.