การบีบอัดตารางละติน


31

ละตินตารางเป็นตารางที่ไม่มีสัญลักษณ์ในแถวหรือคอลัมน์ซ้ำ:

13420
21304
32041
04213
40132

และอย่างที่ผู้เล่นซูโดกุหลายคนรู้คุณไม่จำเป็นต้องใช้ตัวเลขทั้งหมดเพื่ออนุมานตัวเลขที่เหลืออยู่

ความท้าทายของคุณคือการบีบอัดตารางละตินลงในไม่กี่ไบต์เท่าที่จะทำได้ คุณต้องจัดเตรียมหนึ่งหรือสองโปรแกรมที่บีบอัด / คลายการบีบอัด

ข้อมูลต่างๆ:

  • หมายเลขที่ใช้มักจะเป็น0..N-1ที่NมีความยาวของขอบของตารางและN<=25
  • ในการคลายการบีบอัดตารางละตินต้องเหมือนกับอินพุต
  • โปรแกรมของคุณควรที่จะ (de) บีบอัดลาตินสแควร์ใด ๆ (ภายในขนาดสี่เหลี่ยมจัตุรัสสูงสุด) ไม่ใช่เฉพาะที่ฉันให้ไว้ อัตราส่วนการบีบอัดควรจะคล้ายกันเช่นกัน
  • คุณต้องทำการบีบอัดและคลายการบีบอัดเพื่อให้ได้คะแนนของคุณ (ไม่มีจุดสิ้นสุดของจักรวาล)

กรณีทดสอบสามารถพบได้บนGitHub คะแนนของคุณคือขนาดรวมของกรณีทดสอบที่บีบอัด

แก้ไข: ตั้งแต่วันที่ 20:07 ในวันที่ 7 กรกฎาคมฉันได้อัปเดตกรณีทดสอบ (เพื่อแก้ไขปัญหาการสร้าง) โปรดรันโปรแกรมของคุณอีกครั้งในกรณีทดสอบใหม่ ขอบคุณAnders Kaseorg


1
ดีโดยความหมายใด ๆสัญลักษณ์สามารถนำมาใช้ แต่กรณีทดสอบของฉันเพียงแค่เกิดขึ้นที่จะใช้0แม้ว่าn-1:)
นาธานเมอร์


3
@NathanMerrill ดีจุดได้รับอนุญาตให้ใช้nสัญลักษณ์ที่แตกต่างกัน : P
Martin Ender

1
@DavidC มันไม่ควรเป็นเรื่องเป็นขนาดเป็นวัดในไบต์
ข้อผิดพลาด

2
19 กรณีทดสอบ 25 (ทุกคนยกเว้น 4, 6, 8, 10, 12, 14) ถูกสร้างขึ้นโดย permuting แถวและคอลัมน์ของตารางละตินเล็กน้อยที่มี ( I , J ) รายการคือฉัน + J mod n สิ่งนี้ทำให้พวกเขาบีบอัดได้ง่ายกว่าจัตุรัสละตินแบบสุ่ม แม้ว่ากฎของคุณจะบอกว่าเราควรมีอัตราส่วนการบีบอัดที่คล้ายกันสำหรับสี่เหลี่ยมจตุรัสละตินทั้งหมด แต่ก็อาจจะแตกหักได้โดยง่าย กรณีทดสอบควรเป็นตัวแทนเพิ่มเติม
Anders Kaseorg

คำตอบ:


10

Python 1281.375 1268.625 ไบต์

เราเข้ารหัส "การตัดสินใจ" ละตินตารางหนึ่งที่แต่ละการตัดสินใจเป็นหนึ่งในสามรูปแบบเหล่านี้:

  • หมายเลขใดจะอยู่ในแถวi , คอลัมน์j ;
  • ในแถวiคอลัมน์ใดที่หมายเลขkเข้าไป
  • ในคอลัมน์jแถวใดที่หมายเลขkเข้า

ในแต่ละขั้นตอนเราทำการอนุมานแบบลอจิคัลทั้งหมดที่เราสามารถทำได้จากการตัดสินใจก่อนหน้าจากนั้นเลือกการตัดสินใจด้วยจำนวนทางเลือกที่น้อยที่สุดซึ่งจะใช้จำนวนบิตที่น้อยที่สุดในการเป็นตัวแทน

ตัวเลือกมีให้โดยตัวถอดรหัสเลขคณิตอย่างง่าย (div / mod ตามจำนวนตัวเลือก) แต่นั่นทำให้เหลือความซ้ำซ้อนในการเข้ารหัส: ถ้าkถอดรหัสเป็นสี่เหลี่ยมจัตุรัสซึ่งผลคูณของตัวเลือกทั้งหมดคือmดังนั้นk + 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 แต่เพิ่งให้ข้อผิดพลาดรันไทม์ ฉันแก้ไขมันโดยใช้ stdin แทนไฟล์ f ideone.com/fKGSQd
edc65

@ edc65 ใช้งานไม่ได้เพราะ NumPy ของ Ideone ล้าสมัย
Dennis

@ edc65 Ideone มี NumPy 1.8.2 np.stack()ซึ่งเป็นแก่เกินไปสำหรับ ในกรณีนี้มันสามารถถูกแทนที่ด้วยnp.array([…])และฉันได้ทำในรุ่นปัจจุบัน
Anders Kaseorg

อืมม สี่เหลี่ยมทั้งหมดถูกเก็บไว้ในหนึ่งสตรีมไบท์หรือไม่ ข้อมูลเกี่ยวกับขนาดของมันยังถูกเก็บไว้หรือเครื่องถอดรหัสถือว่ามันมีขนาด 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 คุณจะแยกแยะคำที่มีความยาวต่างกันได้อย่างไร? เท่าที่ฉันรู้ว่าคุณต้องมีคำนำหน้าเพิ่มเติมใช่ไหม?
ข้อบกพร่อง

ไม่ใช่ตัวแปรภายในการบีบอัดเดียวเช่นขึ้นกับขนาดของสแควร์ ถ้า n> 16 ให้ใช้ 5 บิตที่คงที่ถ้า 8 <n <= 16 ใช้ 4 บิตที่ได้รับการแก้ไขเป็นต้น
Karl Napf

โอ้ถูกต้องแล้วขอขอบคุณ!
ข้อบกพร่อง

3
ด้วยเหตุผลเดียวกันกับที่คุณทำไปในทางตรงกันข้ามมันอาจเป็นไปได้ที่คุณคุ้นเคย =)
ข้อผิดพลาด

7

Python 3, 1,0772 บิต (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 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 แต่ละสตริงจะถูกรวมเข้าด้วยกันโดยใช้อักขระ ASCII ที่ 255

ขยายขนาด 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

ลบแถว + คอลัมน์สุดท้ายและซิปที่เหลือ

  • แก้ไข 1: base64ดูเหมือนไม่จำเป็น
  • แก้ไข 2: ตอนนี้แปลงตารางสับเป็นไบนารีสตริงและบีบอัดหากจำเป็นเท่านั้น

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 - 3,572 3,581 ไบต์

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 ใช้รายการ tuples จำนวนเต็มและส่งคืนสตริง

dateDeCompress รับสตริงและส่งคืนรายการของจำนวนเต็ม tuples

ในระยะสั้นสำหรับแต่ละบรรทัดโปรแกรมนี้ใช้ดัชนีการเรียงลำดับบรรทัดนั้นและบันทึกในฐาน 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ส่งคืนตัวกำเนิดซึ่งเกียจคร้านสร้างการเรียงสับเปลี่ยนทั้งหมด แต่ถ้าคุณพยายามทำให้มันกลายเป็น a listมันจะสร้างการเรียงสับเปลี่ยนทั้งหมดที่กระตือรือร้น เป็นเวลานานมาก
Mego

คุณช่วยอธิบายวิธีใช้รหัสของคุณให้ดีขึ้นได้ไหม?
Mego

@ เล็กน้อยแน่นอนฉันอาจจะใช้การประเมินผลแบบขี้เกียจด้วยแม้ว่ามันจะยังไม่สามารถเอาชนะได้
Yytsi


3
นอกจากนี้ยังมีวิธีที่มีประสิทธิภาพสำหรับการคำนวณการเปลี่ยนแปลงให้ดัชนี
Mego

1

Java, 2310 ไบต์

เราแปลงแต่ละแถวของสี่เหลี่ยมจัตุรัสเป็นตัวเลขที่แสดงถึงการเปลี่ยนแปลงขนาดเล็กโดยใช้ตัวเลขตัวประกอบหรือที่เรียกว่าระบบตัวเลขแฟคทอเรียลซึ่งมีประโยชน์สำหรับการเรียงลำดับเลข

เราเขียนสี่เหลี่ยมจัตุรัสเป็นไฟล์ไบนารีโดยที่ไบต์แรกคือขนาดของสี่เหลี่ยมจัตุรัสแล้วแต่ละแถวมีหนึ่งไบต์สำหรับจำนวนไบต์ในการแทนค่าไบนารี่ของ Java BigInteger ตามด้วยไบต์ของ 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.