Os dados aleatórios de


19

Tenho dados reais que estou usando para um jogo de cartas simulado. Estou interessado apenas nas fileiras das cartas, não nos naipes. No entanto, é um baralho de cartas padrão , portanto, existem apenas de cada nível possível no baralho. O baralho é bem baralhado para cada mão e, em seguida, mostro o baralho inteiro em um arquivo. Então, existem somente possíveis símbolos no arquivo de saída que são . ( = dez rank). Então, é claro que podemos compactar esses bits usando bits por símbolo, mas estamos desperdiçando das codificações possíveis. Podemos fazer melhor se agruparmos símbolos por vez e depois compactá-los, porque524132,3,4,5,6,7,8,9,T,J,Q,K,AT43164134 = e isso pode caber "confortavelmente" em bits em vez de . O limite teórico de empacotamento de bits é log ( ) / log ( ) = para dados com símbolos aleatórios para cada cartão possível. No entanto, não podemos ter reis, por exemplo, neste baralho. DEVEMOS ter apenas de cada classificação em cada deck, para que a codificação da entropia caia cerca de meio bit por símbolo para cerca de .28,56115161323.70044135243.2

Ok, então aqui está o que estou pensando. Esses dados não são totalmente aleatórios. Sabemos que existem de cada nível, portanto, em cada bloco de cartas (chame de baralho embaralhado), para que possamos fazer várias suposições e otimizações. Uma delas é que não precisamos codificar a última placa, porque saberemos o que deve ser. Outra economia seria se terminarmos em uma única fila; por exemplo, se as últimas cartas do baralho forem , não precisaríamos codificá-las porque o decodificador contaria as cartas até esse ponto e verificaria que todas as outras fileiras foram preenchidas e assumirá as " cartões em falta "são todos os s.452377737

Portanto, minha pergunta neste site é: que outras otimizações são possíveis para obter um arquivo de saída ainda menor nesse tipo de dados e, se as usarmos, podemos vencer a entropia teórica (simples) de empacotamento de de 3.700443.70044 bits por símbolo ou mesmo aproximar-se do limite final de entropia de cerca de bits por símbolo em média? Se sim, como?3.2

Quando uso um programa do tipo ZIP (WinZip, por exemplo), vejo apenas uma compactação , o que me diz que está apenas fazendo um bitpack "preguiçoso" para bits. Se eu pré-compactar os dados usando meu próprio pacote de bits, parece que é melhor assim, porque quando eu executo isso em um programa zip, estou tendo um pouco mais compressão . O que estou pensando é: por que não fazer toda a compactação (porque tenho mais conhecimento dos dados do que o programa Zip). Gostaria de saber se posso vencer a entropia "limite" de log ( ) / log ( ) =2:142:11323.70044. Eu suspeito que posso com os poucos "truques" que mencionei e mais alguns que provavelmente posso descobrir. Obviamente, o arquivo de saída não precisa ser "legível por humanos". Desde que a codificação seja sem perdas, ela é válida.

Aqui está um link para milhões de decks embaralhados legíveis por humanos ( por linha). Qualquer um pode "praticar" em um pequeno subconjunto dessas linhas e depois deixar rasgar o arquivo inteiro. Continuarei atualizando meu melhor (menor) tamanho de arquivo com base nesses dados.31

https://drive.google.com/file/d/0BweDAVsuCEM1amhsNmFITnEwd2s/view

A propósito, caso você esteja interessado em que tipo de jogo de cartas esses dados são usados, aqui está o link para minha pergunta ativa (com recompensa de pontos). Foi-me dito que é um problema difícil de resolver (exatamente), pois exigiria uma quantidade enorme de espaço de armazenamento de dados. Várias simulações concordam com as probabilidades aproximadas. Nenhuma solução puramente matemática foi fornecida (ainda). É muito difícil, eu acho.300

/math/1882705/probability-2-player-card-game-with-multiple-patterns-to-win-who-has-the-advantage

Eu tenho um bom algoritmo que está mostrando 168 bits para codificar o primeiro deck nos meus dados de amostra. Esses dados foram gerados aleatoriamente usando o algoritmo de embaralhamento de Fisher-Yates. São dados aleatórios reais, então meu algoritmo recém-criado parece estar funcionando MUITO bem, o que me deixa feliz.

Em relação ao "desafio" da compressão, atualmente estou em cerca de 160 bits por deck. Acho que posso descer para talvez 158. Sim, tentei e obtive 158,43 bits por deck. Eu acho que estou chegando perto do limite do meu algoritmo, então consegui cair abaixo de 166 bits por deck, mas não consegui 156 bits, o que seria 3 bits por cartão, mas foi um exercício divertido. Talvez no futuro eu pense em algo para reduzir em média cada baralho em 2,43 bits ou mais.


8
Se você está gerando esses baralhos embaralhados (em vez de descrever o estado de um baralho físico, por exemplo), não precisa armazenar o baralho - apenas armazene a semente RNG que gerou o baralho.
Jasonharper

3
Sua descrição e as respostas são muito semelhantes a um conceito conhecido como codificação por alcance ( en.wikipedia.org/wiki/Range_encoding ). Você adapta as possibilidades após cada cartão, para que ele reflita os demais cartões possíveis.
H. Idden 18/08/16

Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Gilles 'SO- stop be evil' (

Respostas:


3

Outra coisa a considerar: se você apenas se preocupa em compactar um conjunto completo de vários milhões de decks e também não se importa com a ordem em que eles estão, você pode obter flexibilidade adicional de codificação descartando as informações sobre a ordem do conjunto de decks . Esse seria o caso, por exemplo, se você precisar carregar o conjunto para enumerar todos os decks e processá-los, mas não se importe em que ordem eles serão processados.

Você começa codificando cada baralho individualmente, como outras respostas descreveram como fazer. Em seguida, classifique esses valores codificados. Armazene uma série de diferenças entre os valores codificados classificados (onde a primeira diferença começa no baralho codificado '0'). Dado um grande número de decks, as diferenças tendem a ser menores que o intervalo completo de codificação, portanto, você pode usar alguma forma de codificação de varint para lidar com grandes diferenças ocasionais e ainda assim armazenar as diferenças menores com eficiência. O esquema de varint apropriado dependeria de quantos decks você possui no conjunto (determinando assim o tamanho da diferença média).

Infelizmente, eu não sei a matemática de quanto isso ajudaria sua compressão, mas achei que essa idéia poderia ser útil a considerar.


1
Em termos gerais, se você tiver vários milhões de decks aleatórios, as diferenças médias serão um (vários milionésimos) de toda a faixa, o que significa que você espera economizar cerca de 20 e poucos bits por valor. Você perde um pouco pela codificação varint.
21416 Steve Joplin

2
@ DavidJames: se a ordem específica dos decks não for importante, apenas para que não haja viés, você poderá embaralhar novamente os 3 milhões de decks após a descompressão (ou seja, não altere nenhum dos decks, apenas altere a ordem de a lista de 3 milhões de decks).
21816 Steve Jessop

2
Essa é apenas uma maneira de reduzir um pouco mais o conteúdo das informações, se as informações de pedidos não forem importantes; se for importante, isso não é aplicável e pode ser ignorado. Dito isto, se a única importância para a ordem do conjunto de decks for 'aleatória', você poderá aleatoriamente a ordem após a descompressão, como @SteveJessop afirmou.
Dan Bryant

@DavidJames Ver os primeiros 173 de seus decks começarem com KKKK, e não olhar para os outros milhões, e concluir que todos começam com KKKK, é uma coisa muito estúpida de se fazer. Especialmente se eles estiverem obviamente em uma ordem classificada.
user253751

3
@ DavidJames: esses dados são compactados, e a rotina de descompressão pode re-randomizá-los, se desejado. "Uma pessoa ingênua" não vai conseguir nada, nem vai entender como interpretar isso como baralhos de cartas. É não uma falha em um formato de armazenamento de dados (neste caso um formato lossy), que se alguém utilizar ele precisa RTFM para obter os dados corretos para fora.
21816 Steve Jessop

34

Aqui está um algoritmo completo que atinge o limite teórico.

Prólogo: Codificando Sequências Inteiras

Uma sequência de 13 números inteiros "número inteiro com limite superior , número inteiro com limite superior b - 1 " número inteiro com limite superior c - 1 , número inteiro com limite superior d - 1 , ... número inteiro com limite superior m - 1 " sempre pode ser codificado com eficiência perfeita.uma-1b-1c-1d-1m-1

  1. Pegue o primeiro inteiro, multiplique por , adicione o segundo, multiplique o resultado por c , adicione o terceiro, multiplique o resultado por d ,… multiplique o resultado por m , adicione o décimo terceiro - e isso produzirá um número único entre 0 e a b c d e f g h i j k l m - 1 .bcdm0 0umabcdefghEujkeum-1
  2. Anote esse número em binário.

O inverso também é fácil. Divida por restante é o décimo terceiro número inteiro. Divida o resultado por l e o restante é o décimo segundo número inteiro. Continue até você ter dividido por b : o restante é o segundo inteiro e o quociente é o primeiro inteiro.meub

Portanto, para codificar seus cartões da melhor maneira possível, tudo o que precisamos fazer é encontrar uma correspondência perfeita entre seqüências de 13 números inteiros (com limites superiores determinados) e a organização dos cartões embaralhados.

Aqui está como fazê-lo.

Correspondência entre embaralhamento e seqüências inteiras

Comece com uma sequência de 0 cartas na mesa à sua frente.

Passo 1

Pegue os quatro 2s na sua mochila e coloque-os sobre a mesa.

Que escolhas você tem? Um cartão ou cartões podem ser colocados no início da sequência já na mesa ou após qualquer um dos cartões nessa sequência. Nesse caso, isso significa que existem lugares possíveis para colocar cartões.1+0 0=1

O número total de maneiras de colocar 4 cartas em 1 lugar é . Codifique cada uma dessas maneiras como um número entre 0 e 1 - 1 . Existe um número desse tipo.10 01-1

Eu obtive 1 considerando as formas de escrever 0 como a soma de 5 números inteiros: é .4×3×2×14!

Passo 2

Pegue os quatro 3s na sua mochila e coloque-os sobre a mesa.

Que escolhas você tem? Um cartão ou cartões podem ser colocados no início da sequência já na mesa ou após qualquer um dos cartões nessa sequência. Nesse caso, isso significa que existem locais possíveis para colocar cartões.1+4=5

O número total de maneiras de colocar 4 cartas em 5 lugares é . Codifique cada uma dessas maneiras como um número entre 0 e 70 - 1 . Existem 70 desses números.700701

Eu consegui 70 considerando as formas de escrever 4 como a soma de 5 números inteiros: é .8×7×6×54!

etapa 3

Pegue os quatro 4s na sua mochila e coloque-os sobre a mesa.

Que escolhas você tem? Um cartão ou cartões podem ser colocados no início da sequência já na mesa ou após qualquer um dos cartões nessa sequência. Nesse caso, isso significa que existem locais possíveis para colocar cartões.1+8=9

O número total de maneiras de colocar 4 cartas em 9 lugares é . Codifique cada uma dessas maneiras como um número entre 0 e 495 - 1 . Existem 495 tais números.49504951

Eu obtive 495 considerando as formas de escrever 8 como a soma de 5 números inteiros: é .12×11×10×94!

E assim por diante, até ...

Etapa 13

Pegue os quatro ases na sua mochila e coloque-os sobre a mesa.

Que escolhas você tem? Um cartão ou cartões podem ser colocados no início da sequência já na mesa ou após qualquer um dos cartões nessa sequência. Nesse caso, isso significa que existem locais possíveis para colocar cartões.1+48=49

O número total de maneiras de colocar 4 cartas em 49 lugares é . Codifique cada uma dessas maneiras como um número entre 0 e 270725 - 1 . Existem 270725 esses números.27072502707251

Eu obtive 270725 considerando as maneiras de escrever 48 como a soma de 5 números inteiros: é .52×51×50×494!


Esse procedimento gera uma correspondência 1-para-1 entre (a) baralhar cartas onde você não se importa com o naipe e (b) seqüências de números inteiros em que a primeira está entre e 1 - 1 , a segunda entre 0 e 70 - 1 , o terceiro está entre 0 e 495 - 1 , e assim por diante até o décimo terceiro, que está entre 0 e 270725 - 1 .01107010495102707251

Referindo-se a "Codificando sequências inteiras", você pode ver que essa sequência de números inteiros está em correspondência 1-1 com os números entre e ( 1 × 70 × 495 × × 270725 ) - 1 . Se você observar a expressão "produto dividido por um fatorial" de cada um dos números inteiros ( conforme descrito em itálico no final de cada etapa ), verá que isso significa os números entre 0 e 52 !0(1×70×495××270725)10que minha resposta anterior mostrou foi a melhor possível.

52!(4!)131,

Portanto, temos um método perfeito para compactar seus cartões embaralhados.


O algoritmo

Precompute uma lista de todas as maneiras de escrever 0 como a soma de 5 números inteiros, de escrever 4 como a soma de 5 números inteiros, de escrever 8 como a soma de 5 números inteiros,… de escrever 48 como a soma de 5 números inteiros. A lista mais longa possui 270725 elementos, portanto, não é particularmente grande. (A pré-computação não é estritamente necessária, porque você pode sintetizar facilmente cada lista como e quando necessário: tentar com o Microsoft QuickBasic, até mesmo passar pela lista de elementos 270725 era mais rápido do que os olhos podiam ver.

Para passar de uma ordem aleatória para uma sequência de números inteiros:

Os 2s não contribuem com nada, então vamos ignorá-los. Anote um número entre 0 e 1-1.

Os 3s: Quantos 2s existem antes dos 3 primeiros? Quantos antes do segundo? o terceiro? o quarto? depois do quarto? A resposta é 5 números inteiros que obviamente somam 4. Portanto, procure a sequência de 5 números inteiros na lista "escrevendo 4 como a soma de 5 números inteiros" e observe sua posição nessa lista. Esse será um número entre 0 e 70-1. Anotá-la.

Os 4s: Quantos 2s ou 3s existem antes dos 4 primeiros? Quantos antes do segundo? o terceiro? o quarto? depois do quarto? A resposta é 5 números inteiros que obviamente somam 8. Portanto, procure a sequência de 5 números inteiros na lista "escrevendo 8 como a soma de 5 números inteiros" e observe sua posição nessa lista. Esse será um número entre 0 e 495-1. Anotá-la.

E assim por diante, até ...

Os ases: Quantas cartas não-ás existem antes do primeiro ás? Quantos antes do segundo? o terceiro? o quarto? depois do quarto? A resposta é 5 números inteiros que obviamente somam 48. Portanto, procure a sequência de 5 números inteiros na lista "escrevendo 48 como a soma de 5 números inteiros" e observe sua posição nessa lista. Esse será um número entre 0 e 270725-1. Anotá-la.

Você já anotou 13 números inteiros. Codifique-os (como descrito anteriormente) em um único número entre e 52 !0 . Escreva esse número em binário. Demorará pouco menos de 166 bits.52!(4!)13

Essa é a melhor compactação possível, pois atinge o limite teórico da informação.

A descompressão é direta: vá do número grande até a sequência de 13 números inteiros e use-os para criar a sequência de cartões como já descrito.


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
DW

Esta solução não é clara para mim e incompleta. Ele não mostra como obter o número de 166 bits e decodificá-lo de volta para o baralho. Não é nada fácil conceber para mim, então não saberei como implementá-lo. Sua fórmula escalonada basicamente desmonta os fórmula em 13 peças, o que realmente não me ajuda muito. Acho que teria ajudado se você fizesse um diagrama ou gráfico para talvez o passo 2 com as 70 maneiras possíveis de organizar os cartões. Sua solução é abstrata demais para meu cérebro aceitar e processar. Eu prefiro exemplos e ilustrações reais. 52!/(4!13)13
David James

23

Em vez de tentar codificar cada placa separadamente em 3 ou 4 bits, sugiro que você codifique o estado do baralho inteiro em 166 bits. Como Martin Kochanski explica , existem menos de disposições possíveis das cartas que ignoram naipes, o que significa que o estado de todo o baralho pode ser armazenado em 166 bits.2166

Como você faz essa compressão e descompressão algoritmicamente, de maneira eficiente? Sugiro o uso de pedidos lexicográficos e pesquisa binária. Isso permitirá que você faça compactação e descompactação com eficiência (no espaço e no tempo), sem exigir uma grande tabela de pesquisa ou outras suposições irrealistas.

Mais detalhadamente: vamos pedir decks usando a ordem lexicográfica na representação não compactada do deck, ou seja, um deck é representado na forma descompactada como uma string como 22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA; você pode solicitá-los de acordo com a ordem lexicográfica. Agora, suponha que você tenha um procedimento que, dado um baralho , conte o número de baralhos que vêm antes dele (em ordem lexicográfica). Em seguida, você pode usar este procedimento para compactar um baralho: dado um baralho D , você compactará para um número de 166 bits contando o número de baralhos que vêm antes dele e, em seguida, exibindo esse número. Esse número é a representação compactada do baralho.DD

Para descompactar, use a pesquisa binária. Dado um número , você quer encontrar o n º baralho na ordenação lexicográfica de todos os decks. Você pode fazer isso usando um procedimento ao longo das linhas de pesquisa binária: escolha um deck D 0 , conte o número de decks antes de D 0 e compare isso com n . Isso informará se você deve ajustar D 0nnD0D0nD0para vir mais cedo ou mais tarde. Sugiro que você tente acertar iterativamente o símbolo: se você deseja recuperar uma string como 22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA, primeiro procure o que usar como o primeiro símbolo na string (tente todas as 12 possibilidades ou use a pesquisa binária nas 12 possibilidades ), depois de encontrar o valor correto para o primeiro símbolo, procure o segundo símbolo e assim por diante.

Tudo o que resta é para chegar a um procedimento eficiente para contar o número de plataformas que vêm lexicographically antes . Parece um exercício combinatório direto, mas tedioso. Em particular, sugiro que você crie uma sub-rotina para o seguinte problema: dado um prefixo (como 222234), conte o número de decks que começam com esse prefixo. A resposta para esse problema parece um exercício bastante fácil de coeficientes e fatoriais binomiais. Em seguida, você pode chamar essa sub-rotina um pequeno número de vezes para contar o número de plataformas que vêm antes D .DD


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
DW

8

O número de arranjos possíveis das cartas que ignoram naipes é cuja base de logaritmo 2 é 165,976 ou 3,1919 bits por cartão, o que é melhor do que o limite que você forneceu.

52!(4!)13,

Qualquer codificação fixa de "bits por cartão" não fará sentido porque, como você observa, o último cartão sempre pode ser codificado em bits e, em muitos casos, os últimos poucos cartões também. Isso significa que, para um caminho mais próximo da "cauda" do baralho, o número de bits necessários para cada cartão será bem menor do que você pensa.0

De longe, a melhor maneira de compactar os dados seria encontrar 59 bits de outros dados que você deseja compactar com os dados do cartão (59,6 bits, na verdade) e escrever esses 59 bits como um módulo de número de 13 dígitos 24 (= ), Atribua um naipe a cada carta (um dígito escolhe entre as 4 ! Maneiras de atribuir naipes aos ases, outro faz o mesmo para os reis, e assim por diante). Então você tem um baralho de 52 cartas totalmente distintas. 52 ! as possibilidades podem ser codificadas em 225,58 bits com muita facilidade.4!4!52!

Mas fazê-lo sem aproveitar a oportunidade de codificar esses bits extras também é possível até certo ponto, e pensarei nisso como tenho certeza de que todo mundo está. Obrigado por um problema realmente interessante!


1
Uma abordagem semelhante ao roubo de texto cifrado poderia ser usada aqui? Como em, os dados que você codifica nesses 59 bits extras são os últimos 59 bits da representação codificada?
John Dvorak

@ JanD Eu estava pensando em investigar algo assim. Mas, em seguida, verificou-se que existe um algoritmo que atinge o limite teórico e é direto e 100% confiável; portanto, não havia sentido em procurar mais.
Martin Kochanski

@MartinKochanski - Eu não diria isso como "ignoring suits", porque ainda estamos honrando os 4 naipes padrão por classificação. Melhor texto pode ser "O número de arranjos possíveis distintas do deck é" ...
David James

3

Este é um problema há muito resolvido.

Quando você distribui um baralho de 52 cartas, todas as cartas que você distribui têm uma de até 13 classificações com probabilidades conhecidas. As probabilidades mudam com cada cartão negociado. Isso é tratado da melhor maneira possível, usando uma técnica antiga chamada codificação aritmética adaptativa, uma melhoria na codificação de Huffman. Normalmente, isso é usado para probabilidades conhecidas e imutáveis, mas também pode ser usado para alterar probabilidades. Leia o artigo da Wikipedia sobre codificação aritmética:

https://en.wikipedia.org/wiki/Arithmetic_coding


Ok, mas isso não responde à minha pergunta se ele pode se aproximar, corresponder ou superar o limite de codificação da entropia teórica. Parece que, como não há n possíveis decks, cada um com probabilidade de 1 / n, a codificação da entropia é o limite e não podemos fazer melhor (a menos que trapaceiemos e digamos ao decodificador algo sobre os dados de entrada para o codificador antes do tempo.
David James

3

DW e Martin Kochanski já descreveram algoritmos para construir uma bijeção entre transações e números inteiros no intervalo , mas parece que nenhum deles reduziu o problema à sua forma mais simples. (Nota 1)[0,52!(4!)13)

Suponha que tenhamos um baralho (parcial) descrito pela lista ordenada , onde a i é o número de cartas do tipo i . No PO, o deck inicial é descrito por uma lista de 13 elementos, cada um com o valor 4. O número de shuffles distintos desse deck éaaii

c(a)=(ai)!ai!

que é uma simples generalização dos coeficientes binomiais e, de fato, poderia ser comprovada simplesmente organizando os objetos um tipo de cada vez, conforme sugerido por Martin Kochanski. (Veja abaixo, nota 2)

Agora, para qualquer baralho (parcial), podemos selecionar uma carta aleatória de cada vez, usando qualquer para o qual um i > 0 . O número de aleatórias únicas que começam com i éiai>0i

{0if ai=0c(a1,...,ai1,ai1,ai+1,...,an)if ai>0.

e pela fórmula acima, temos

c(a1,...,ai1,ai1,ai+1,...,an)=aic(a)ai

Podemos recursar (ou iterar) pelo baralho até que o shuffle esteja completo, observando que o número de shuffles correspondentes a um prefixo lexicograficamente menor que o prefixo até éi

c(a)j=1iajj=1naj

Eu escrevi isso em Python para ilustrar o algoritmo; Python é um pseudocódigo tão razoável quanto qualquer outro. Observe que a maior parte da aritmética envolve precisão estendida; os valores (representando o ordinal do shuffle) e n (o número total de shuffles possíveis para o restante deck parcial) são ambos bignums de 166 bits. Para traduzir o código para outro idioma, será necessário usar algum tipo de biblioteca bignum.kn

Além disso, eu apenas uso a lista de números inteiros em vez de nomes de cartões e - ao contrário da matemática acima - os números inteiros são baseados em 0.

Para codificar um shuffle, percorremos o shuffle, acumulando em cada ponto o número de shuffles que começam com um cartão menor usando a fórmula acima:

from math import factorial
T = factorial(52) // factorial(4) ** 13

def encode(vec):
    a = [4] * 13
    cards = sum(a)
    n = T
    k = 0
    for idx in vec:
        k += sum(a[:idx]) * n // cards
        n = a[idx] * n // cards
        a[idx] -= 1
        cards -= 1
    return k

Decodificar um número de 166 bits é o inverso simples. Em cada etapa, temos a descrição de um baralho parcial e um ordinal; precisamos pular os shuffles que começam com cartões menores do que o que corresponde ao ordinal e calculamos a saída do card selecionado, removemos-o do baralho restante e ajustamos o número de shuffles possíveis com o prefixo selecionado:

def decode(k):
    vec = []
    a = [4] * 13
    cards = sum(a)
    n = T
    while cards > 0:
        i = cards * k // n
        accum = 0
        for idx in range(len(a)):
            if i < accum + a[idx]:
                k -= accum * n // cards
                n = a[idx] * n // cards
                a[idx] -= 1
                vec.append(idx)
                break
            accum += a[idx]
        cards -= 1
    return vec

Não fiz nenhuma tentativa real de otimizar o código acima. Eu o executei no arquivo 3mil.TXT inteiro, verificando se isso encode(decode(line))resultou na codificação original; demorou pouco menos de 300 segundos. (Sete das linhas podem ser vistas no teste on-line de ideona .) Reescrever em um idioma de nível inferior e otimizar a divisão (o que é possível) provavelmente reduziria esse tempo a algo gerenciável.

Como o valor codificado é simplesmente um número inteiro, ele pode ser emitido em 166 bits. Não há valor em excluir os zeros à esquerda, pois não haveria como saber onde uma codificação terminou, portanto, é realmente uma codificação de 166 bits.

No entanto, vale a pena notar que, em uma aplicação prática, provavelmente nunca é necessário codificar um shuffle; um shuffle aleatório pode ser gerado gerando um número aleatório de 166 bits e decodificando-o. E não é realmente necessário que todos os 166 bits sejam aleatórios; seria possível, por exemplo, começar com um número inteiro aleatório de 32 bits e preencher os 166 bits usando qualquer RNG padrão semeado com o número de 32 bits. Portanto, se o objetivo é simplesmente poder armazenar um número grande de reproduções aleatórias aleatoriamente, você pode reduzir o requisito de armazenamento por transação mais ou menos arbitrariamente.

Se você deseja codificar um grande número de transações reais (geradas de alguma outra maneira), mas não se importa com a ordem das transações, pode codificar em delta a lista classificada de números, economizando aproximadamente 2 N bits de log para cada número. (A economia resulta do fato de uma sequência classificada ter menos entropia do que uma sequência não classificada. Não reduz a entropia de um único valor na sequência.)Nlog2N

Supondo que precisamos codificar uma lista classificada de números de k bits, podemos proceder da seguinte maneira:N k

  1. Escolha como um número inteiro próximo ao log 2 N (o piso ou o teto funcionará; eu geralmente uso o teto).plog2N

  2. Dividimos implicitamente o intervalo de números em intervalos de pelo prefixo binário. Cada número de k- bits é dividido em um prefixo de p- bits e um sufixo de k - p- bits; escrevemos apenas os sufixos (em ordem). Isso requer N ( k - p ) bits.2pkpkpN(kp)

  3. Além disso, criamos uma sequência de bits: para cada um dos prefixos p (exceto o prefixo 0 ), escrevemos um 0 para cada número com esse prefixo (se houver) seguido por 1 . Esta sequência tem, obviamente, 2 p + N bits de: 2 p 1 s e N 0 s.2p0012p+N2p 1N 0

Para decodificar os números, iniciamos um contador de prefixo em 0 e prosseguimos com o trabalho na sequência de bits. Quando vemos um , produzimos o prefixo atual e o próximo sufixo da lista de sufixos; quando vemos 1 , incrementamos o prefixo atual.01

O comprimento total da codificação é que é muito próximo a N ( k - p ) + N + N , ou N ( k - p + 2 ) , para uma média de k - p + 2 bits por valor.N(kp)+N+2pN(kp)+N+NN(kp+2)kp+2

Notas

  1. é92024242230271040357108320801872044844750000000000eregistre252!52!(4!)1392024242230271040357108320801872044844750000000000 é aproximadamente165,9765. No texto, ocasionalmente finjo que o logaritmo da base 2 é realmente166; no caso de gerar ordinais aleatórios dentro do intervalo, poderia ser usado um algoritmo de rejeição que apenas raramente rejeitaria um número aleatório gerado.log252!(4!)13165.9765166
  2. Por conveniência, escrevo para n i = k a i ; em seguida, a um 1 objectos de tipo 1 pode ser colocada em ( S 1Ski=knaia11maneiras, e então os objetos do tipo2podem ser colocados em(S2(S1a1)2maneiras, e assim por diante. Desde ( Si(S2a2), isso leva à contagem total(Siai)=Si!ai!(Siai)!=Si!ai!Si+1!

i=1nSi!i=1nai!Si+1!

o que simplifica a fórmula acima.


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
DW

@rici - Eu dei a você +100 de recompensa, porque você explicou sua resposta no que parece ser uma apresentação melhor, incluindo código, enquanto as outras respostas são mais abstratas / teóricas, deixando de fora alguns detalhes de como realmente implementar a codificação / decodificação. Como você deve saber, há muitos detalhes ao escrever código. Admito que meu algoritmo também não é o mais direto, simples e fácil de entender, mas na verdade consegui fazê-lo funcionar sem muito esforço e, com o tempo, posso fazê-lo funcionar mais rápido com mais compactação. Então, obrigado pela sua resposta e continue com o bom trabalho.
David James

2

Como uma solução alternativa para esse problema, meu algoritmo usa bits fracionários compostos (não inteiros) por cartão para grupos de cartões no baralho com base em quantas fileiras não preenchidas ainda existem. É um algoritmo bastante elegante. Eu verifiquei meu algoritmo de codificação manualmente e está com boa aparência. O codificador está emitindo o que parece ser uma sequência de bits correta (em formato de bytes, para simplificar).

A visão geral do meu algoritmo é que ele usa uma combinação de grupos de cartões e codificação de bits fracionários compostos. Por exemplo, no meu arquivo de teste compartilhada de milhões de baralhos, o primeiro tem os primeiros 7 cartões de 54 A 236 J . A razão pela qual escolhi um tamanho de bloco de 7 cartas quando 13 fileiras de cartas são possíveis é porque 13 7 "calçadeiras" (se encaixam perfeitamente) em 26 bits (desde 13 7 = 62 , 748 , 517 e 2 26 = 67 , 108 ,3754A236J7131372613762,748,517226 ). Idealmente, queremos que esses 2 números sejam o mais próximo possível (mas com a potência do número 2 um pouco maior), para não desperdiçar mais do que uma fração muito pequena de um pouco no processo de empacotamento de bits. Observe que eu também poderia ter escolhido o tamanho do grupo 4 ao codificar 13 classificações desde 13 4 = 28 , 561 e 2 15 = 32 , 768 . Não é tão apertado um ajuste desde 15 / 4 = 3,75 , mas 26 de / 7 = 3.71467,108,864241313428,56121532,76815/4=3.7526/7=3.714. Assim, o número de bits por cartão é um pouco menor por cartão se usarmos a método de embalagem.26/7

Então, olhando para , simplesmente procuramos a posição ordinal dessas classificações em nossa lista principal " 23456789 T J Q K A " de classificações. Por exemplo, o primeiro ranking de cartas real de 5 tem uma posição de pesquisa na sequência de pesquisa de ranking de 4 . Apenas tratamos essas 7 posições de classificação como um número base 13 começando com 0 (então a posição 4 que obtivemos anteriormente será realmente um 3). Convertido de volta à base 10 (para fins de verificação), obtemos 15 , 565 , 975 . Em 2654A236J23456789TJQKA547131015,565,97526bits de binário obtemos .00111011011000010010010111

O decodificador funciona de maneira muito semelhante. Ele pega (por exemplo) essa sequência de bits e a converte novamente em decimal (base 10) para obter 15 , 565 , 975 , depois a converte na base 13 para obter as compensações na sequência de pesquisa de classificação e depois reconstrói as classificações um de cada vez e recebe as primeiras 7 cartas originais 54 A 236 J. Observe que o tamanho do bloco de bits nem sempre será 26, mas sempre começará em 26 em cada deck. O codificador e o decodificador têm algumas informações importantes sobre os dados do deck antes mesmo de operar. Essa é uma coisa excepcionalmente agradável sobre esse algoritmo.2615,565,9751354A236J7

Cada # de fileiras restante (tal como tem o seu próprio groupsize e custo (# de bits por cartão). Estes foram encontrados experimentalmente apenas brincando com potências de 13 , 12 , 11 ... e potências de 2 . Eu já expliquei como consegui o tamanho do grupo para quando podemos ver 13 classificações, então que tal cairmos para 12 classificações não preenchidas? Mesmo método. Observe as potências de 12 e pare quando uma delas estiver muito próxima de uma potência de 2, mas apenas um pouco abaixo dela. 13,12,11...,2,1)13,12,11...21312122 = 248 , 832 e 2 18 = 262 , 144 . Esse é um ajuste bem apertado. O número de bits que codificam este grupo é 18 de / 5 = 3,6 . No 13 grupo posto era 26 de / 7 = 3,714 , de modo que possa ver, como o número de fileiras não preenchidas diminui (fileiras estão enchendo tais como 5555 , 3333 ), o número de bits para codificar os cartões diminui.125248,832218262,14418/53.61326/73.71455553333

Aqui está minha lista completa de custos (número de bits por cartão) para todos os possíveis números de classificações a serem vistos:

12 18 de / 5 = 3,600 = 3 3 / 5 11 7 / 2 = 3,500 = 3 1 / 2 10 10 / 3 = 3,333 = 3 1 / 3 9 16 / 5 = 3,200 = 3 1 / 5 8 3 / 113    26/7=3.714=3  5/7
12    18/5=3.600=3  3/5
11      7/2=3.500=3  1/2
10    10/3=3.333=3  1/3
  9    16/5=3.200=3  1/5
7 17 / 6 = 2.833 = 2 5 / 6 6 13 / 5 = 2600 = 2 3 / 5 5 7 / 3 = 2.333 = 2 1 / 3 4 2 / 1 = 2.000 = 2 3 5 / 3 = 1,667 = 1 2 / 3 2 1 /  8      3/1=3.000=3
  7    17/6=2.833=2  5/6
  6    13/5=2.600=2  3/5
  5      7/3=2.333=2  1/3
  4      2/1=2.000=2
  3      5/3=1.667=1  2/3
1 0 / 1..4 = 0,0 = 0  2      1/1=1.000=1
  1      0/1..4=0.0=0

75,6,7,7,7,7,KK1312713K21,2,3...3131720

16813,12,11

10777748747s. Se o baralho terminar em um par (como 77), triplo / set (como 777) ou quad (como 7777), obteremos economias adicionais para esse baralho usando meu algoritmo.

3222613163232

No primeiro baralho no arquivo de dados, a codificação dos cartões é a seguinte (diagrama a seguir). O formato é (tamanho do grupo, bits, modo de codificação de classificação):

7,26,1372613
7,26,13
7,26,13
5,18,12
5,18,12
3,10,10
3,  9,  8
6,17,  7
5,13,  6
3,  5,  3
1,  0,  1

521683.23

181/33.23.254545454722772277...322223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA40

1103,7K8101cartão restante. Isso é importante porque torna o processo de codificação mais eficiente quando o decodificador pode fazer suposições corretas sem que o codificador precise passar mensagens extras para ele.

313121110

         26             26             26            18         18       10      9          17           13        5     0
    54UMA236J  87726Q3  3969UMAUMAUMA  QJK7T  9292Q  36.K  J57   T8TKJ4  48.Q8T  55K  4
13                                            12                    xy     98         7              6        543     2 1  0 0

2166175168bits. Observe que nós só temos um 4 no final do baralho, mas se tivermos todos os quatro 4s lá, esse é um caso melhor e precisaríamos de apenas 161 bits para codificar esse baralho, um caso em que a embalagem realmente bate o entropia de uma codificação binária direta da posição ordinal dela.

Agora tenho o código implementado para calcular os requisitos de bit e ele está mostrando, em média, cerca de 175 bits por deck com um mínimo de 155 e um máximo de 183 para o arquivo de teste de 3 milhões de deck. Portanto, meu algoritmo parece usar 9 bits extras por deck versus a codificação binária direta do método da posição ordinal. Não é tão ruim com apenas 5,5% de espaço de armazenamento adicional necessário. 176 bits são exatamente 22 bytes, o que é um pouco melhor que 52 bytes por deck. Pacotes de melhor case (não apareceu em 3 milhões de arquivos de teste de deck) com 136 bits e pior cenário (apareceu no arquivo de teste 8206 vezes) é de 183 bits. A análise mostra que o pior caso é quando não obtemos o primeiro quad até perto da (ou na) placa 40. Então, como o modo de codificação quer cair rapidamente, somos "presos" blocos de preenchimento (tão grandes quanto 7 placas) em um modo de codificação de bit mais alto. Pode-se pensar que não obter quads até o card 40 seria bastante raro usando um baralho bem baralhado, mas meu programa está me dizendo que aconteceu 321 vezes no arquivo de teste de 3 milhões de decks, de modo que cerca de 1 em cada 9346 decks. Isso é mais frequente do que eu esperava. Eu poderia verificar esse caso e lidar com menos bits, mas é tão raro que não afetaria os bits médios o suficiente.

Também aqui está algo muito interessante. Se eu classificar o baralho nos dados brutos do baralho, o tamanho dos prefixos que repetem um número significativo de vezes é apenas do tamanho 6 (como 222244). No entanto, com os dados compactados, esse comprimento aumenta para cerca de 16. Isso significa que, se eu classificar os dados compactados, conseguirá uma economia significativa apenas indicando ao decodificador um prefixo de 16 bits e, em seguida, apenas exibindo o restante dos decks (menos o prefixo de repetição) que possui o mesmo prefixo, vá para o próximo prefixo e repita. Supondo que eu economize apenas 10 bits por deck dessa maneira, devo vencer os 166 bits por deck. Com a técnica de enumeração declarada por outros, não tenho certeza se o prefixo seria tão longo quanto no meu algoritmo. Além disso, a velocidade de empacotar e descompactar usando meu algoritmo é surpreendentemente boa.

No que diz respeito ao segundo nível de compactação em que classifico as seqüências de bits de saída do meu algoritmo, em seguida, uso a codificação "diferença": Um método muito simples seria codificar os 61.278 prefixos exclusivos de 16 bits que aparecem pelo menos duas vezes nos dados de saída (e um máximo 89 vezes relatado) simplesmente como um bit inicial de 0 na saída para indicar ao descompressor de segundo nível que estamos codificando um prefixo (como 0000111100001111) e, em seguida, todos os decks compactados com o mesmo prefixo seguirão com 1 bit inicial para indique a parte sem prefixo do baralho embalado. O número médio de decks compactados com o mesmo prefixo é de cerca de 49 para cada prefixo, sem incluir os poucos que são únicos (apenas 1 deck possui esse prefixo específico). Parece que posso economizar cerca de 15 bits por deck usando essa estratégia simples (armazenando os prefixos comuns uma vez).

Após o segundo nível de compactação usando a codificação de diferença (prefixo) da saída classificada em bitstring do primeiro codificador, agora estou obtendo cerca de 160 bits por deck. Eu uso o prefixo do comprimento 18 e apenas o armazeno intacto. Como quase todos (245013 de 262144 = 93,5%) desses possíveis prefixos de 18 bits são exibidos, seria ainda melhor codificar os prefixos. Talvez eu possa usar 2 bits para codificar que tipo de dados eu tenho. 00 = prefixo 18 de tamanho normal armazenado, 01 = "prefixo 1 up" (igual ao prefixo anterior, exceto 1 adicionado), 11 = codificação direta a partir do empacotamento do 1º nível (aproximadamente 175 bits em média). 10 = expansão futura quando penso em outra coisa para codificar que economizará bits.

Já alguém bateu 160 bits por baralho? Acho que posso diminuir um pouco o meu com algumas experiências e o uso dos descritores de 2 bits que mencionei acima. Talvez isso chegue ao fim. Meu objetivo é chegar a 156 bits (ou melhor), porque seriam 3 bits por cartão ou menos. Muito impressionante. Muitas experiências para chegar a esse nível, porque se eu mudar a codificação do primeiro nível, tenho que testar novamente qual é a melhor codificação do segundo nível e há muitas combinações para tentar. Algumas alterações que eu faço podem ser boas para outros dados aleatórios semelhantes, mas algumas podem ser tendenciosas em relação a esse conjunto de dados. Não tenho muita certeza, mas se eu tiver vontade, posso tentar outro conjunto de dados de 3 milhões de decks para ver como acontece se eu obtiver resultados semelhantes.

1050.

Alguém tem alguma idéia de como melhorar meu algoritmo, como em outros casos que eu deveria codificar que reduziriam, em média, bits de armazenamento para cada deck? Qualquer um?

Mais duas coisas: 1) Estou um pouco decepcionado com o fato de mais pessoas não terem votado na minha solução, que apesar de não ser ótima no espaço, ainda é decente e bastante fácil de implementar (fiz a minha funcionar bem). 2) Fiz uma análise no meu arquivo de dados de 3 milhões de baralhos e notei que o cartão que ocorre com mais frequência onde o 1º ranking ocupa (como 4444) é o cartão 26. Isso acontece cerca de 6,711% do tempo (em 201322 dos 3 milhões de baralhos ) Eu esperava usar essas informações para comprimir mais, como iniciar no modo de codificação de 12 símbolos, pois sabemos que, em média, não veremos todas as classificações até o meio do dia, mas esse método não conseguiu compactar, pois a sobrecarga excedeu a economia. Estou procurando por alguns ajustes no meu algoritmo que podem realmente salvar bits.

Alguém tem alguma idéia do que devo tentar a seguir para economizar alguns bits por deck usando meu algoritmo? Estou procurando por um padrão que ocorra com frequência suficiente para que eu possa reduzir os bits por baralho, mesmo após a sobrecarga extra de informar ao decodificador qual padrão esperar. Eu estava pensando em algo com as probabilidades esperadas dos restantes cartões invisíveis e agrupando todos os restantes em um único balde. Isso me permitirá entrar em um modo de codificação inferior mais rápido e talvez salvar alguns bits, mas duvido.

Além disso, para sua informação, gerei 10 milhões de shuffles aleatórios e os armazenei em um banco de dados para facilitar a análise. Apenas 488 deles terminam em um quadrilátero (como o 5555). Se eu empacotar apenas aqueles que usam meu algoritmo, recebo 165.71712 bits, em média, com um mínimo de 157 bits e um máximo de 173 bits. Um pouco abaixo dos 166 bits usando o outro método de codificação. Estou um pouco surpreso com a raridade deste caso (cerca de 1 em cada 20.492 embaralhamentos em média).


3
Percebo que você fez cerca de 24 edições no espaço de 9 horas. Agradeço seu desejo de melhorar sua resposta. No entanto, sempre que você edita a resposta, ela é exibida no topo da primeira página. Por esse motivo, desencorajamos a edição excessiva. Se você espera fazer muitas edições, seria possível agrupar suas edições, para que você faça apenas uma edição a cada poucas horas? (Aliás, nota que colocar "EDIT:" e "UPDATE:" em sua resposta geralmente é estilo pobre Veja. Meta.cs.stackexchange.com/q/657/755. )
DW

4
Este não é o lugar para colocar relatórios de progresso, atualizações de status ou itens de blog. Queremos respostas totalmente formadas, não "em breve" ou "Eu tenho uma solução, mas não vou descrever o que é".
DW

3
Se alguém estiver interessado, ele encontrará a solução aprimorada. A melhor maneira é esperar pela resposta completa e publicá-la. Se você tiver algumas atualizações, um blog faria. Não encorajo isso, mas se você realmente deve (não vejo uma razão válida), você pode escrever um comentário abaixo da sua postagem e mesclar mais tarde. Também encorajo você a excluir todos os comentários obsoletos e incorporá-los em uma pergunta contínua - fica difícil ler todos. Eu tento criar meu próprio algoritmo, diferente de qualquer outro apresentado, mas não estou satisfeito com os resultados - por isso não posto parciais a serem editados - a caixa de resposta é para os completos.
mal

3
@DavidJames, eu entendo. No entanto, isso ainda não altera nossas diretrizes: não faça tantas edições. (Se você quiser propor melhorias no site, sinta-se à vontade para fazer uma postagem no nosso Computer Science Meta ou no meta.stackexchange.com sugerindo. Os desenvolvedores não leem este tópico de comentário.) Mas, enquanto isso, nós trabalhar com o software que possuímos e fazer muitas edições é desencorajado porque esbarra na questão. Nesse ponto, limitar-se a uma edição por dia pode ser uma boa orientação a se seguir. Sinta-se livre para usar editores offline ou StackEdit, se isso ajudar!
DW

3
Não estou votando sua resposta por vários motivos. 1) é desnecessário por muito tempo e MUITO detalhado demais. Você pode reduzir drasticamente sua apresentação. 2) existem respostas melhores postadas, que você escolhe ignorar por razões que não me são conhecidas. 3) perguntar sobre a falta de votos positivos geralmente é uma "bandeira vermelha" para mim. 4) Isso permaneceu constantemente na primeira página devido a uma quantidade INSANE de edições.
Nicholas Mancuso
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.