Les données aléatoires de


19

J'ai de vraies données que j'utilise pour un jeu de cartes simulé. Je ne m'intéresse qu'aux rangs des cartes, pas aux couleurs. Cependant, il s'agit d'un jeu de cartes standard , il n'y a donc que de chaque rang possible dans le jeu. Le jeu est bien mélangé pour chaque main, puis je renvoie l'ensemble du jeu dans un fichier. Donc , il n'y a que symboles possibles dans le fichier de sortie qui sont . ( = dix rang). Donc, bien sûr, nous pouvons les compresser en utilisant bits par symbole, mais nous gaspillons alors des encodages possibles. Nous pouvons faire mieux si nous groupons symboles à la fois, puis les compressons, car524132,3,4,5,6,7,8,9,T,J,Q,K,AT43164134 = et qui peut tenir plutôt "confortablement" en bits au lieu de . La limite théorique de compression des bits est log ( ) / log ( ) = pour les données avec symboles aléatoires pour chaque carte possible. Cependant nous ne pouvons pas avoir rois par exemple dans ce deck. Nous ne devons avoir que de chaque rang dans chaque deck, de sorte que le codage entropique chute d'environ un demi-bit par symbole à environ .28,56115161323.70044135243.2

Ok, voici donc ce que je pense. Ces données ne sont pas totalement aléatoires. Nous savons qu'il y a de chaque rang, donc dans chaque bloc de cartes (appelez cela un jeu mélangé), nous pouvons donc faire plusieurs hypothèses et optimisations. L'un de ceux-ci étant, nous n'avons pas à encoder la toute dernière carte, car nous saurons ce qu'elle devrait être. Une autre économie serait si nous nous retrouvions sur un seul rang; par exemple, si les dernières cartes du jeu sont , nous n'aurions pas à les coder car le décodeur compterait les cartes jusqu'à ce point et verrait que tous les autres rangs ont été remplis, et supposera les " les cartes manquantes sont toutes de s.452377737

Donc, ma question à ce site est, quelles autres optimisations sont possibles pour obtenir un fichier de sortie encore plus petit sur ce type de données, et si nous les utilisons, pouvons-nous jamais battre l'entropie théorique (simple) de bits de bits par symbole, ou même approcher la limite ultime d'entropie d'environ bits par symbole en moyenne? Si c'est le cas, comment?3.700443.2

Lorsque j'utilise un programme de type ZIP (WinZip par exemple), je ne vois qu'une compression , ce qui me dit qu'il ne fait qu'un bitpack "paresseux" à bits. Si je «précompresse» les données en utilisant mon propre bitpacking, cela semble mieux, car lorsque je l'exécute via un programme zip, j'obtiens un peu plus de compression. Ce que je pense, c'est pourquoi ne pas faire moi-même toute la compression (parce que je connais mieux les données que le programme Zip). Je me demande si je peux battre la "limite" d'entropie de log ( ) / log ( ) =2:142:11323.70044. Je soupçonne que je peux avec les quelques "trucs" que j'ai mentionnés et quelques autres que je peux probablement découvrir. Le fichier de sortie ne doit bien sûr pas être "lisible par l'homme". Tant que l'encodage est sans perte, il est valide.

Voici un lien vers millions de jeux mélangés lisibles par l'homme ( par ligne). N'importe qui peut "s'exercer" sur un petit sous-ensemble de ces lignes, puis le laisser déchirer sur l'ensemble du fichier. Je continuerai à mettre à jour ma meilleure (plus petite) taille de fichier en fonction de ces données.131

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

Au fait, au cas où vous seriez intéressé par le type de jeu de cartes pour lequel ces données sont utilisées, voici le lien vers ma question active (avec prime de points). On me dit que c'est un problème difficile à résoudre (exactement) car cela nécessiterait une énorme quantité d'espace de stockage de données. Cependant, plusieurs simulations concordent avec les probabilités approximatives. Aucune solution purement mathématique n'a encore été apportée. C'est trop dur, je suppose.300

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

J'ai un bon algorithme qui affiche bits pour encoder le premier deck dans mes données d'échantillon. Ces données ont été générées de manière aléatoire en utilisant l'algorithme de shuffle de Fisher-Yates. Ce sont de vraies données aléatoires, donc mon algorithme nouvellement créé semble très bien fonctionner, ce qui me rend heureux.168

Concernant le "challenge" de compression, je suis actuellement à environ 160 bits par platine. Je pense que je peux descendre à peut-être 158. Oui, j'ai essayé et j'ai obtenu 158,43 bits par platine. Je pense que je me rapproche de la limite de mon algorithme, j'ai donc réussi à descendre en dessous de 166 bits par jeu, mais je n'ai pas réussi à obtenir 156 bits, ce qui serait 3 bits par carte, mais c'était un exercice amusant. Peut-être qu'à l'avenir, je penserai à quelque chose pour réduire chaque platine en moyenne de 2,43 bits ou plus.


8
Si vous générez vous-même ces jeux mélangés (plutôt que de décrire l'état d'un jeu de cartes physique, par exemple), vous n'avez pas du tout besoin de stocker le jeu - stockez simplement la graine RNG qui a généré le jeu.
jasonharper

3
Votre description et celles des réponses sont très similaires à un concept communément appelé codage par plage ( en.wikipedia.org/wiki/Range_encoding ). Vous adaptez les propriétés après chaque carte afin qu'elle reflète les cartes possibles restantes.
H.Idden

Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Gilles 'SO- arrête d'être méchant'

Réponses:


3

Une autre chose à considérer: si vous vous souciez uniquement de compresser un ensemble complet de plusieurs millions de decks et que vous ne vous souciez pas non plus de l'ordre dans lequel ils se trouvent, vous pouvez gagner une flexibilité d'encodage supplémentaire en supprimant les informations sur la commande de l'ensemble de decks . Ce serait le cas, par exemple, si vous devez charger l'ensemble pour énumérer tous les decks et les traiter, mais peu importe l'ordre dans lequel ils sont traités.

Vous commencez par encoder chaque deck individuellement, comme d'autres réponses ont décrit comment procéder. Ensuite, triez ces valeurs codées. Stockez une série de différences entre les valeurs codées triées (où la première différence commence à partir du pont codé «0»). Étant donné un grand nombre de decks, les différences auront tendance à être plus petites que la plage de codage complète, vous pouvez donc utiliser une forme de codage varint pour gérer les grandes différences occasionnelles tout en stockant les différences plus petites de manière efficace. Le schéma de varint approprié dépendrait du nombre de decks que vous avez dans l'ensemble (déterminant ainsi la taille moyenne de la différence.)

Je ne sais malheureusement pas à quel point cela pourrait aider votre compression, mais j'ai pensé que cette idée pourrait être utile à considérer.


1
En gros, si vous avez plusieurs millions de decks aléatoires, les différences moyennes seront de un (plusieurs millionième) de la plage complète, ce qui signifie que vous vous attendez à économiser environ 20 bits quelque chose par valeur. Vous perdez un peu pour votre encodage varint.
Steve Jessop

2
@DavidJames: si l'ordre spécifique des decks n'est pas important, juste qu'il n'y a pas de biais, vous pouvez remélanger les 3 millions de decks après la décompression (ie ne changez aucun des decks, changez simplement l'ordre des la liste des 3 millions de ponts).
Steve Jessop

2
C'est juste un moyen de réduire un peu plus le contenu des informations si les informations de commande ne sont pas importantes; s'il est important, cela ne s'applique pas et peut être ignoré. Cela dit, si la seule importance pour l'ordre de l'ensemble de decks est qu'il est `` aléatoire '', vous pouvez simplement randomiser l'ordre après la décompression, comme l'a déclaré @SteveJessop.
Dan Bryant

@DavidJames Voir que les 173 premiers de vos decks commencent par KKKK, et ne regarde pas les autres millions, et conclure qu'ils commencent tous par KKKK, est une chose assez stupide à faire. Surtout s'ils sont évidemment dans un ordre trié.
user253751

3
@DavidJames: ces données sont compressées et la routine de décompression peut les re-randomiser si vous le souhaitez. "Une personne naïve" n'obtiendra rien du tout, ils ne sauront même pas comment l'interpréter comme des jeux de cartes. Ce n'est pas une faille dans un format de stockage de données (dans ce cas, un format avec perte), que quelqu'un qui l'utilise a besoin de RTFM pour obtenir les bonnes données.
Steve Jessop

34

Voici un algorithme complet qui atteint la limite théorique.

Prologue: encodage de séquences entières

Une séquence de 13 entiers "entier avec limite supérieure , entier avec limite supérieure b - 1 ," entier avec limite supérieure c - 1 , entier avec limite supérieure d - 1 , ... entier avec limite supérieure m - 1 " peut toujours être codé avec une efficacité parfaite.a1b1c1d1m1

  1. Prenez le premier entier, multipliez-le par , ajoutez le second, multipliez le résultat par c , ajoutez le troisième, multipliez le résultat par d ,… multipliez le résultat par m , ajoutez le treizième - et cela produira un nombre unique entre 0 et a b c d e f g h i j k l m - 1 .bcdm0abcdefghijklm1
  2. Notez ce nombre en binaire.

L'inverse est également facile. Divisez par et le reste est le treizième entier. Divisez le résultat par l et le reste est le douzième entier. Continuez jusqu'à ce que vous ayez divisé par b : le reste est le deuxième entier et le quotient est le premier entier.mlb

Donc, pour coder vos cartes de la meilleure façon possible, tout ce que nous avons à faire est de trouver une correspondance parfaite entre des séquences de 13 entiers (avec des limites supérieures données) et les dispositions de vos cartes mélangées.

Voici comment faire.

Correspondance entre les réarrangements et les séquences entières

Commencez avec une séquence de 0 cartes sur la table devant vous.

Étape 1

Prenez les quatre 2 dans votre sac et placez-les sur la table.

Quels choix avez-vous? Une ou plusieurs cartes peuvent être placées soit au début de la séquence déjà sur la table, soit après l'une des cartes de cette séquence. Dans ce cas, cela signifie qu'il y a emplacements possibles pour mettre des cartes.1+0=1

Le nombre total de façons de placer 4 cartes à 1 endroits est de . Encodez chacune de ces façons sous la forme d'un nombre compris entre 0 et 1 - 1 . Il y en a 1.1011

J'ai obtenu 1 en considérant les façons d'écrire 0 comme la somme de 5 entiers: c'est .4×3×2×14!

Étape 2

Prenez les quatre 3 dans votre sac et placez-les sur la table.

Quels choix avez-vous? Une ou plusieurs cartes peuvent être placées soit au début de la séquence déjà sur la table, soit après l'une des cartes de cette séquence. Dans ce cas, cela signifie qu'il y a emplacements possibles pour mettre des cartes.1+4=5

Le nombre total de façons de placer 4 cartes à 5 endroits est de . Encodez chacune de ces façons sous la forme d'un nombre compris entre 0 et 70 - 1 . Il y en a 70.700701

J'ai obtenu 70 en considérant les façons d'écrire 4 comme la somme de 5 entiers: c'est .8×7×6×54!

Étape 3

Prenez les quatre 4 dans votre sac et placez-les sur la table.

Quels choix avez-vous? Une ou plusieurs cartes peuvent être placées soit au début de la séquence déjà sur la table, soit après l'une des cartes de cette séquence. Dans ce cas, cela signifie qu'il y a emplacements possibles pour mettre des cartes.1+8=9

Le nombre total de façons de placer 4 cartes à 9 endroits est de . Encodez chacune de ces façons sous la forme d'un nombre compris entre 0 et 495 - 1 . Il en existe 495.49504951

J'ai obtenu 495 en considérant les façons d'écrire 8 comme la somme de 5 entiers: c'est .12×11×10×94!

Et ainsi de suite, jusqu'à ce que ...

Étape 13

Prenez les quatre as dans votre sac et placez-les sur la table.

Quels choix avez-vous? Une ou plusieurs cartes peuvent être placées soit au début de la séquence déjà sur la table, soit après l'une des cartes de cette séquence. Dans ce cas, cela signifie qu'il y a emplacements possibles pour mettre des cartes.1+48=49

Le nombre total de façons de placer 4 cartes à 49 endroits est de . Encodez chacune de ces façons sous la forme d'un nombre compris entre 0 et 270725 - 1 . Il y en a 270725.27072502707251

J'ai obtenu 270725 en considérant les façons d'écrire 48 comme la somme de 5 entiers: c'est .52×51×50×494!


Cette procédure produit une correspondance 1 à 1 entre (a) des mélanges de cartes où vous ne vous souciez pas de la couleur et (b) des séquences d'entiers où le premier est compris entre et 1 - 1 , le second est entre 0 et 70 - 1 , le troisième est compris entre 0 et 495 - 1 , et ainsi de suite jusqu'au treizième, qui est compris entre 0 et 270725 - 1 .01107010495102707251

En se référant à "Encodage de séquences entières", vous pouvez voir qu'une telle séquence d'entiers est en correspondance 1-1 avec les nombres entre et ( 1 × 70 × 495 × × 270725 ) - 1 . Si vous regardez l'expression "produit divisé par une factorielle" de chacun des entiers ( comme décrit en italique à la fin de chaque étape ), vous verrez que cela signifie les nombres entre 0 et 52 !0(1×70×495××270725)10ce que ma réponse précédente a montré était le meilleur possible.

52!(4!)131,

Nous avons donc une méthode parfaite pour compresser vos cartes mélangées.


L'algorithme

Précalculez une liste de toutes les façons d'écrire 0 comme la somme de 5 entiers, d'écrire 4 comme la somme de 5 entiers, d'écrire 8 comme la somme de 5 entiers,… d'écrire 48 comme la somme de 5 entiers. La liste la plus longue contient 270725 éléments, elle n'est donc pas particulièrement longue. (Le pré-calcul n'est pas strictement nécessaire car vous pouvez facilement synthétiser chaque liste au fur et à mesure de vos besoins: essayer avec Microsoft QuickBasic, même parcourir la liste des éléments 270725 était plus rapide que l'œil ne pouvait le voir)

Pour passer d'un mélange à une séquence d'entiers:

Les 2 ne contribuent à rien, alors ignorons-les. Notez un nombre entre 0 et 1-1.

Les 3: Combien y a-t-il de 2 avant les 3 premiers? Combien avant la seconde? le troisième? le 4? après le 4? La réponse est 5 entiers, ce qui correspond évidemment à 4. Alors recherchez cette séquence de 5 entiers dans votre liste "écrit 4 comme la somme de 5 entiers" et notez sa position dans cette liste. Ce sera un nombre compris entre 0 et 70-1. Écris le.

Les 4: Combien y a - t-il de 2 ou 3 avant les 4 premiers? Combien avant la seconde? le troisième? le 4? après le 4? La réponse est 5 nombres entiers qui, évidemment, totalisent 8. Donc, recherchez cette séquence de 5 nombres entiers dans votre liste "écrire 8 comme la somme de 5 nombres entiers" et notez sa position dans cette liste. Ce sera un nombre compris entre 0 et 495-1. Écris le.

Et ainsi de suite, jusqu'à ce que…

Les as: Combien de cartes non-as y a-t-il avant le premier as? Combien avant la seconde? le troisième? le 4? après le 4? La réponse est 5 nombres entiers qui totalisent évidemment 48. Recherchez donc cette séquence de 5 nombres entiers dans votre liste "écrire 48 comme la somme de 5 nombres entiers" et notez sa position dans cette liste. Ce sera un nombre compris entre 0 et 270725-1. Écris le.

Vous avez maintenant noté 13 entiers. Encodez-les (comme décrit précédemment) en un seul numéro entre et 52 !0 . Écrivez ce nombre en binaire. Cela prendra un peu moins de 166 bits.52!(4!)13

Il s'agit de la meilleure compression possible, car elle atteint la limite théorique de l'information.

La décompression est simple: passez du grand nombre à la séquence de 13 entiers, puis utilisez-les pour construire la séquence de cartes comme déjà décrit.


Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
DW

Cette solution n'est pas claire pour moi et incomplète. Il ne montre pas comment obtenir réellement le nombre de 166 bits et le décoder dans la platine. Ce n'est pas facile du tout de concevoir pour moi donc je ne sais pas comment le mettre en œuvre. Votre formule étagée démonte simplement les formule en 13 morceaux ce qui ne m'aide vraiment pas beaucoup. Je pense que cela aurait aidé si vous aviez fait un diagramme ou un graphique pour peut-être l'étape 2 avec les 70 façons possibles d'organiser les cartes. Votre solution est trop abstraite pour que mon cerveau puisse l'accepter et la traiter. Je préfère des exemples et des illustrations réels. 52!/(4!13)13
David James

23

Plutôt que d'essayer d'encoder chaque carte séparément en 3 ou 4 bits, je vous suggère de coder l'état de l'ensemble du jeu en 166 bits. Comme Martin Kochanski explique , il y a moins de arrangements possibles des cartes ignorant costumes, de sorte que les moyens de l'état du pont entier peut être stocké dans 166 morceaux.2166

Comment effectuez-vous cette compression et décompression de manière algorithmique, de manière efficace? Je suggère d'utiliser l'ordre lexicographique et la recherche binaire. Cela vous permettra d'effectuer efficacement la compression et la décompression (à la fois dans l'espace et dans le temps), sans nécessiter une grande table de recherche ou d'autres hypothèses irréalistes.

Plus en détail: Ordonnons les decks en utilisant l'ordre lexicographique sur la représentation non compressée du deck, c'est-à-dire qu'un deck est représenté sous forme non compressée sous la forme d'une chaîne comme 22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA; vous pouvez les commander selon l'ordre lexicographique. Supposons maintenant que vous ayez une procédure qui, étant donné un deck , compte le nombre de decks qui le précèdent (dans l'ordre lexicographique). Ensuite, vous pouvez utiliser cette procédure pour compresser un deck: étant donné un deck D , vous compressez à un nombre de 166 bits en comptant le nombre de decks qui le précèdent puis en sortant ce nombre. Ce nombre est la représentation compressée du jeu.DD

Pour décompresser, utilisez la recherche binaire. Étant donné un nombre , vous voulez trouver le n ème jeu dans l'ordre lexicographique de tous les jeux. Vous pouvez le faire en utilisant une procédure dans le sens de la recherche binaire: choisissez un jeu D 0 , comptez le nombre de jeux avant D 0 et comparez-le à n . Cela vous dira si vous souhaitez ajuster D 0nnD0D0nD0à venir plus tôt ou plus tard. Je vous suggère d'essayer d'obtenir le symbole de manière itérative: si vous souhaitez récupérer une chaîne comme 22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA, commencez par rechercher pour trouver quoi utiliser comme premier symbole de la chaîne (essayez simplement les 12 possibilités ou utilisez la recherche binaire sur les 12 possibilités) ), puis lorsque vous avez trouvé la bonne valeur pour le premier symbole, recherchez le deuxième symbole, etc.

Tout ce qui reste est de trouver une procédure efficace pour compter le nombre de ponts qui viennent lexicographique avant . Cela ressemble à un exercice combinatoire simple mais fastidieux. En particulier, je vous suggère de créer un sous-programme pour le problème suivant: étant donné un préfixe (comme 222234), comptez le nombre de ponts qui commencent par ce préfixe. La réponse à ce problème ressemble à un exercice assez facile de coefficients binomiaux et factoriels. Ensuite, vous pouvez appeler ce sous - programme un petit nombre de fois pour compter le nombre de ponts qui viennent avant D .DD


Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
DW

8

Le nombre d'arrangements possibles des cartes ignorant les combinaisons est de dont la base de logarithme 2 est 165,976, soit 3,1919 bits par carte, ce qui est mieux que la limite que vous avez donnée.

52!(4!)13,

Tout fixe le codage « bits par carte » ne sera pas de sens parce que, comme vous le notez, la dernière carte peut toujours être codée en bits , et dans de nombreux cas , les dernières quelques cartes peuvent être aussi bien. Cela signifie que pour un certain chemin vers la "queue" du pack, le nombre de bits nécessaires pour chaque carte sera bien inférieur à ce que vous pensez.0

De loin, la meilleure façon de compresser les données serait de trouver de toute façon 59 bits d'autres données que vous souhaitez emballer avec vos données de carte (59,6 bits, en fait) et, en écrivant ces 59 bits sous la forme d'un nombre à 13 chiffres modulo 24 (= ), Attribuez une couleur à chaque carte (un chiffre choisit entre les 4 ! Façons d'affecter des couleurs aux as, un autre fait de même pour les rois, etc.). Ensuite, vous avez un pack de 52 cartes totalement distinctes. 52 ! les possibilités peuvent être encodées en 225,58 bits très facilement.4!4!52!

Mais le faire sans saisir l'occasion de coder ces bits supplémentaires est également possible dans une certaine mesure, et j'y penserai comme je suis sûr que tout le monde l'est. Merci pour un problème vraiment intéressant!


1
Une approche similaire au vol de texte chiffré pourrait-elle être utilisée ici? Comme dans, les données que vous encodez dans ces 59 bits supplémentaires sont les 59 derniers bits de la représentation codée?
John Dvorak

@JanD Je pensais à enquêter sur quelque chose comme ça. Mais il s'est avéré qu'il existe un algorithme qui atteint la limite théorique et qui est simple et fiable à 100%, il était donc inutile de chercher plus loin.
Martin Kochanski

@MartinKochanski - Je ne dirais pas que "ignorer les costumes" parce que nous honorons toujours les 4 costumes standard par rang. Une meilleure formulation pourrait être "Le nombre d'arrangements distincts possibles du jeu est" ...
David James

3

Il s'agit d'un problème résolu depuis longtemps.

Lorsque vous distribuez un jeu de 52 cartes, chaque carte que vous distribuez possède l'un des 13 rangs avec les probabilités connues. Les probabilités changent avec chaque carte distribuée. Cela est géré de manière optimale en utilisant une technique ancienne appelée codage arithmétique adaptatif, une amélioration du codage Huffman. Habituellement, cela est utilisé pour les probabilités connues et immuables, mais il peut tout aussi bien être utilisé pour changer les probabilités. Lisez l'article de wikipedia sur le codage arithmétique:

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


D'accord, mais cela ne répond pas à ma question s'il peut s'approcher, correspondre ou battre la limite de codage entropique théorique. Il semble qu'il y ait n decks possibles chacun avec une probabilité de 1 / n, alors le codage entropique est la limite et nous ne pouvons pas faire mieux (sauf si nous «trichons» et disons au décodeur quelque chose sur les données d'entrée au codeur à l'avance.
David James

3

DW et Martin Kochanski ont déjà décrit des algorithmes pour construire une bijection entre des offres et des entiers dans la plage , mais il semble qu'aucun d'eux n'ait réduit le problème à sa forme la plus simple. (Note 1)[0,52!(4!)13)

Supposons que nous ayons un jeu (partiel) décrit par la liste ordonnée , où a i est le nombre de cartes de type i . Dans le PO, le jeu initial est décrit par une liste de 13 éléments, chacun avec la valeur 4. Le nombre de shuffles distincts d'un tel jeu estaaii

c(a)=(ai)!ai!

qui est une simple généralisation des coefficients binomiaux, et pourrait en effet être prouvée en disposant simplement les objets un type à la fois, comme l'a suggéré Martin Kochanski. (Voir ci-dessous, note 2)

Maintenant, pour toute plate - forme (partielle), on peut sélectionner une lecture aléatoire d' une carte à la fois, en utilisant tout pour lequel un i > 0 . Le nombre de shuffles uniques commençant par i estiai>0i

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

et par la formule ci-dessus, nous avons

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

Nous pouvons ensuite recurse (ou itérer) à travers le jeu jusqu'à ce que le shuffle soit terminé en observant que le nombre de shuffles correspondant à un préfixe lexicographiquement plus petit que le préfixe jusqu'à esti

c(a)j=1iajj=1naj

J'ai écrit ceci en Python pour illustrer l'algorithme; Python est un pseudocode aussi raisonnable que n'importe quel autre. Notez que la plupart de l'arithmétique implique une précision étendue; les valeurs (représentant l'ordinal du shuffle) et n (le nombre total de shuffles possibles pour le paquet partiel restant) sont toutes deux des bignums de 166 bits. Pour traduire le code dans une autre langue, il sera nécessaire d'utiliser une sorte de bibliothèque bignum.kn

De plus, j'utilise simplement une liste d'entiers plutôt que des noms de carte, et - contrairement aux calculs ci-dessus - les entiers sont basés sur 0.

Pour encoder un shuffle, nous parcourons le shuffle, en accumulant à chaque point le nombre de shuffles qui commencent par une carte plus petite en utilisant la formule ci-dessus:

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

Le décodage d'un nombre de 166 bits est l'inverse simple. À chaque étape, nous avons la description d'un paquet partiel et d'un ordinal; nous devons sauter les shuffles en commençant par des cartes plus petites que celle qui correspond à l'ordinal, puis nous calculons la sortie de la carte sélectionnée, la supprimons du jeu restant et ajustons le nombre de shuffles possibles avec le préfixe sélectionné:

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

Je n'ai pas vraiment tenté d'optimiser le code ci-dessus. Je l'ai exécuté sur l'intégralité du fichier 3mil.TXT, en vérifiant qu'il en encode(decode(line))résultait l'encodage d'origine; cela a pris un peu moins de 300 secondes. (Sept des lignes peuvent être vues dans le test en ligne sur ideone .) La réécriture dans un langage de niveau inférieur et l'optimisation de la division (ce qui est possible) réduirait probablement ce temps à quelque chose de gérable.

Comme la valeur codée est simplement un entier, elle peut être sortie en 166 bits. Il n'y a aucune valeur à supprimer les zéros de tête, car il n'y aurait alors aucun moyen de savoir où un codage s'est terminé, il s'agit donc vraiment d'un codage de 166 bits.

Cependant, il convient de noter que dans une application pratique, il n'est probablement jamais nécessaire de coder un shuffle; un mélange aléatoire peut être généré en générant un nombre aléatoire de 166 bits et en le décodant. Et il n'est pas vraiment nécessaire que les 166 bits soient aléatoires; il serait possible, par exemple, de commencer avec un entier aléatoire de 32 bits, puis de remplir les 166 bits en utilisant n'importe quel RNG standard prédéfini avec le nombre de 32 bits. Donc, si l'objectif est simplement de pouvoir stocker de manière reproductible un grand nombre de shuffles aléatoires, vous pouvez réduire plus ou moins arbitrairement l'exigence de stockage par transaction.

Si vous souhaitez coder un grand nombre de transactions réelles (générées d'une autre manière) mais que vous ne vous souciez pas de l'ordre des transactions, vous pouvez coder en delta la liste triée de nombres, en économisant environ 2 N bits de journal pour chaque nombre. (Les économies résultent du fait qu'une séquence triée a moins d'entropie qu'une séquence non triée. Elle ne réduit pas l'entropie d'une valeur unique dans la séquence.)Nlog2N

En supposant que nous devons coder une liste triée de k bits, nous pouvons procéder comme suit:N k

  1. Choisissez comme un entier proche de log 2 N (soit le sol, soit le plafond fonctionnera; j'opte généralement pour le plafond).plog2N

  2. Nous divisons implicitement la plage de nombres en intervalles de par préfixe binaire. Chaque numéro à k bits est divisé en un préfixe à p bits et un suffixe à k - p bits; nous n'écrivons que les suffixes (dans l'ordre). Cela nécessite N ( k - p ) bits.2pkpkpN(kp)

  3. De plus, nous créons une séquence de bits: pour chacun des préfixes (sauf le préfixe 0 ), nous écrivons un 0 pour chaque numéro avec ce préfixe (le cas échéant) suivi d'un 1 . Cette séquence comporte évidemment 2 p + N bits: 2 p 1 s et N 0 s.2p0012p+N2p 1N 0

Pour décoder les nombres, nous commençons un compteur de préfixes à 0 et procédons à la séquence de bits. Quand nous voyons un , nous sortons le préfixe actuel et le suffixe suivant de la liste des suffixes; lorsque nous voyons un 1 , nous incrémentons le préfixe actuel.01

La longueur totale du codage est qui est très proche de N ( k - p ) + N + N , ou N ( k - p + 2 ) , pour une moyenne de k - p + 2 bits par valeur.N(kp)+N+2pN(kp)+N+NN(kp+2)kp+2

Remarques

  1. est92024242230271040357108320801872044844750000000000etlog252!52!(4!)1392024242230271040357108320801872044844750000000000 est environ165,9765. Dans le texte, je prétends parfois que le logarithme en base 2 est vraiment166; dans le cas de la génération d'ordinaires aléatoires dans la plage, un algorithme de rejet pourrait être utilisé qui ne rejetterait que très rarement un nombre aléatoire généré.log252!(4!)13165.9765166
  2. Par commodité, j'écris pour n i = k a i ; puis l' un 1 des objets de type 1 peuvent être placés dans ( S 1Ski=knaia11façons, puis les objets de type2peuvent être placés dans(S2(S1a1)2façons, et ainsi de suite. Depuis ( Si(S2a2), ce qui conduit au nombre total(Siai)=Si!ai!(Siai)!=Si!ai!Si+1!

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

ce qui simplifie la formule ci-dessus.


Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
DW

@rici - Je vous ai donné la prime de +100 parce que vous avez expliqué votre réponse dans ce qui semble être une meilleure présentation, y compris du code, tandis que les autres réponses sont plus abstraites / théoriques, en laissant de côté certains détails sur la façon de mettre en œuvre le codage / décodage. Comme vous le savez peut-être, il existe de nombreux détails lors de l'écriture de code. J'admets que mon algorithme n'est pas non plus le plus simple, le plus simple et le plus facile à comprendre non plus, mais je l'ai fait fonctionner sans trop d'effort et avec le temps, je peux le faire fonctionner plus rapidement avec plus de compression. Merci donc pour votre réponse et continuez votre bon travail.
David James

2

Comme solution alternative à ce problème, mon algorithme utilise des bits fractionnaires composés (non entiers) par carte pour des groupes de cartes dans le jeu en fonction du nombre de rangs non remplis qui restent. C'est un algorithme assez élégant. J'ai vérifié mon algorithme d'encodage à la main et il a l'air bien. L'encodeur sort ce qui semble être des chaînes de bits correctes (sous forme d'octets pour plus de simplicité).

3754A236J7131372613762,748,51722667,108,864241313428,56121532,76815/4=3.7526/7=3.71426/7

54A236J23456789TJQKA547131015,565,9752600111011011000010010010111

2615,565,9751354A236J7

13,12,11...,2,1)13,12,11...21312122125248,832218262,14418/53.61326/73.71455553333

Voici ma liste complète des coûts (# de bits par carte) pour tous les # possibles de rangs à voir:

13    26/7=3.714=3  5/7
12    18/5=3,600=3  3/5
11      sept/2=3.500=3  1/2
dix    dix/3=3,333=3  1/3
  9    16/5=3.200=3  1/5
  8      3/1=3.000=3
  sept    17/6=2,833=2  5/6
  6    13/5=2.600=2  3/5
  5      sept/3=2,333=2  1/3
  4      2/1=2.000=2
  3      5/3=1,667=1  2/3
  2      1/1=1.000=1
  1      0/1..4=0,0=0

sept5,6,sept,sept,sept,sept,KK1312sept13K21,2,3 ...3131sept20

16813,12,11

10777748sept4septs. Si le jeu se termine sur une paire (comme 77), triple / set (comme 777) ou un quad (comme 7777), nous obtenons des économies supplémentaires pour ce jeu en utilisant mon algorithme.

3222613163232

Dans le premier deck du fichier de données, l'encodage des cartes est le suivant (schéma à venir plus tard). Le format est (taille de groupe, bits, mode d'encodage de rang):

sept,26,13sept2613
sept,26,13
sept,26,13
5,18,12
5,18,12
3,dix,dix
3,  9,  8
6,17,  sept
5,13,  6
3,  5,  3
1,  0,  1

521683.23

181/33.23.254545454722772277 ...322223333444455556666777788889999TTTTJJJJQQQQKKKKUNEUNEUNEUNE40

1dix3,septK8dix1carte restante. Ceci est important car il rend le processus d'encodage plus efficace lorsque le décodeur peut émettre des hypothèses correctes sans que l'encodeur n'ait à lui transmettre des messages supplémentaires.

3131211dix

         26             26             26            18         18       dix      9          17           13        5     0
    54UNE236J  87726Q3  3969UNEUNEUNE  QJKseptT  9292Q  36K  J57   T8TKJ4  48Q8T  55K  4
13                                            12                    Xy     98         sept              6        543     2 1  0

2166175168morceaux. Notez que nous n'avons obtenu qu'un seul 4 à la fin du jeu, mais si au lieu de cela, nous avons tous les quatre 4, c'est un meilleur cas et nous n'aurions eu besoin que de 161 bits pour coder ce jeu, un cas où l'emballage bat réellement le entropie d'un codage binaire droit de la position ordinale de celui-ci.

J'ai maintenant le code implémenté pour calculer les exigences en bits et il me montre en moyenne environ 175 bits par deck avec un minimum de 155 et un maximum de 183 pour le fichier de test de 3 millions de deck. Mon algorithme semble donc utiliser 9 bits supplémentaires par jeu par rapport au codage binaire droit de la méthode de position ordinale. Pas trop mal avec seulement 5,5% d'espace de stockage supplémentaire requis. 176 bits est exactement 22 octets, ce qui est un peu mieux que 52 octets par platine. Le meilleur paquet de cas (n'apparaissait pas dans 3 millions de fichiers de test de paquet) contient 136 bits et le pire cas (il est apparu dans le fichier de test 8206 fois), est de 183 bits. L'analyse montre que le pire des cas est lorsque nous n'obtenons le premier quadruple que près (ou au niveau) de la carte 40. Ensuite, comme le mode d'encodage veut tomber rapidement, nous sommes "bloqués" en remplissant des blocs (jusqu'à 7 cartes) dans un mode de codage de bits supérieur. On pourrait penser que ne pas obtenir de quads avant la carte 40 serait assez rare en utilisant un deck bien mélangé, mais mon programme me dit que cela s'est produit 321 fois dans le fichier de test de 3 millions de decks, ce qui fait environ 1 sur 9346 decks. C'est plus souvent que je m'y attendais. Je pourrais vérifier ce cas et le gérer avec moins de bits, mais il est si rare qu'il n'affecterait pas suffisamment les bits moyens.

Voici aussi autre chose de très intéressant. Si je trie le jeu sur les données brutes du jeu, la longueur des préfixes qui se répètent un nombre significatif de fois n'est que de la longueur 6 (comme 222244). Cependant, avec les données compressées, cette longueur augmente à environ 16. Cela signifie que si je trie les données compressées, je devrais être en mesure de réaliser des économies importantes en indiquant simplement au décodeur un préfixe de 16 bits, puis en sortant simplement le reste des ponts (moins le préfixe répétitif) qui ont le même préfixe, puis passez au préfixe suivant et répétez. En supposant que j'économise même seulement 10 bits par deck de cette façon, je devrais battre les 166 bits par deck. Avec la technique d'énumération indiquée par d'autres, je ne sais pas si le préfixe serait aussi long qu'avec mon algorithme. De plus, la vitesse d'emballage et de déballage en utilisant mon algorithme est étonnamment bonne.

Concernant le 2ème niveau de compression où je trie les chaînes de bits de sortie de mon algorithme, puis j'utilise un codage "différence": Une méthode très simple serait de coder les 61.278 préfixes 16 bits uniques qui apparaissent au moins deux fois dans les données de sortie (et un maximum de 89 fois rapporté) simplement comme un bit de tête de 0 dans la sortie pour indiquer au décompresseur de 2e niveau que nous encodons un préfixe (tel que 0000111100001111), puis tous les ponts emballés avec ce même préfixe suivront avec un bit de tête de 1 à indiquer la partie non préfixe du paquet emballé. Le nombre moyen de decks emballés avec le même préfixe est d'environ 49 pour chaque préfixe, sans compter les quelques uns qui sont uniques (seulement 1 deck a ce préfixe particulier). Il semble que je puisse économiser environ 15 bits par jeu en utilisant cette stratégie simple (en stockant une fois les préfixes communs).

Après le 2ème niveau de compression utilisant le codage de différence (préfixe) de la sortie de chaîne de bits triée du premier codeur, j'obtiens maintenant environ 160 bits par platine. J'utilise le préfixe de longueur 18 et je le stocke intact. Étant donné que presque tous (245013 sur 262144 = 93,5%) de ces préfixes 18 bits possibles apparaissent, il serait encore mieux de coder les préfixes. Je peux peut-être utiliser 2 bits pour coder le type de données dont je dispose. 00 = longueur régulière 18 préfixe stocké, 01 = "1 préfixe supérieur" (identique au préfixe précédent sauf 1 ajouté), 11 = codage direct à partir d'un emballage de 1er niveau (environ 175 bits en moyenne). 10 = extension future quand je pense à autre chose à encoder qui sauvera des bits.

Quelqu'un d'autre a-t-il déjà battu 160 bits par jeu? Je pense que je peux obtenir le mien un peu plus bas avec quelques expériences et en utilisant les descripteurs 2 bits que j'ai mentionnés ci-dessus. Peut-être qu'il atteindra un creux à 158ish. Mon objectif est de l'amener à 156 bits (ou mieux) car ce serait 3 bits par carte ou moins. Très impressionnant. Beaucoup d'expérimentations pour descendre à ce niveau car si je change l'encodage de premier niveau, je dois retester qui est le meilleur encodage de 2ème niveau et il y a beaucoup de combinaisons à essayer. Certaines modifications que je fais peuvent être bonnes pour d'autres données aléatoires similaires, mais certaines peuvent être biaisées vers cet ensemble de données. Pas vraiment sûr, mais si j'ai envie, je peux essayer un autre ensemble de données de 3 millions de jeux pour voir ce qui se passe si j'obtiens des résultats similaires.

dix50

Quelqu'un a-t-il des idées sur la façon d'améliorer mon algorithme, comme les autres cas que je devrais encoder, ce qui réduirait en moyenne les bits de stockage pour chaque deck? N'importe qui?

2 autres choses: 1) Je suis quelque peu déçu que plus de gens n'aient pas voté pour ma solution qui, bien qu'elle ne soit pas optimale dans l'espace, est toujours décente et assez facile à mettre en œuvre (la mienne fonctionnait bien). 2) J'ai fait une analyse sur mon fichier de données de 3 millions de deck et j'ai remarqué que la carte la plus fréquente où le premier rang se remplit (comme 4444) est à la carte 26. Cela se produit environ 6,711% du temps (pour 201322 des 3 millions de decks ). J'espérais utiliser ces informations pour compresser davantage, comme commencer en mode d'encodage à 12 symboles, car nous savons qu'en moyenne, nous ne verrons pas tous les rangs jusqu'à environ middeck, mais cette méthode n'a pas réussi à compresser tout car les frais généraux dépassaient les économies. Je cherche quelques ajustements à mon algorithme qui peuvent réellement sauver des bits.

Alors, quelqu'un a-t-il des idées sur ce que je devrais essayer ensuite pour économiser quelques bits par jeu en utilisant mon algorithme? Je recherche un modèle qui se produit assez fréquemment pour que je puisse réduire les bits par platine même après la surcharge supplémentaire de dire au décodeur quel modèle à attendre. Je pensais à quelque chose avec les probabilités attendues des cartes invisibles restantes et je regroupais toutes les cartes restantes dans un seul seau. Cela me permettra de passer plus rapidement en mode d'encodage inférieur et peut-être d'enregistrer quelques bits mais j'en doute.

Aussi, pour info, j'ai généré 10 millions de shuffles aléatoires et les ai stockés dans une base de données pour une analyse facile. Seuls 488 d'entre eux se terminent en quad (comme 5555). Si je n'emballe que ceux qui utilisent mon algorithme, j'obtiens 165.71712 bits en moyenne avec un minimum de 157 bits et un maximum de 173 bits. Juste légèrement en dessous des 166 bits en utilisant l'autre méthode de codage. Je suis quelque peu surpris de la rareté de ce cas (environ 1 sur 20 492 shuffles en moyenne).


3
Je remarque que vous avez effectué environ 24 modifications en 9 heures. J'apprécie votre désir d'améliorer votre réponse. Cependant, chaque fois que vous modifiez la réponse, cela la place en haut de la page d'accueil. Pour cette raison, nous déconseillons un montage excessif. Si vous prévoyez d'effectuer de nombreuses modifications, serait-il possible de regrouper vos modifications par lots, de sorte que vous ne fassiez qu'une seule modification toutes les quelques heures? ( Soit dit en passant , notez que mettre "EDIT:" et "UPDATE:" dans votre réponse est généralement un style médiocre. Voir meta.cs.stackexchange.com/q/657/755. )
DW

4
Ce n'est pas l'endroit pour mettre des rapports d'avancement, des mises à jour de statut ou des articles de blog. Nous voulons des réponses complètes, pas "à venir" ou "j'ai une solution mais je ne vais pas décrire ce que c'est".
DW

3
Si quelqu'un est intéressé, il trouvera la solution améliorée. La meilleure façon est d'attendre la réponse complète et de l'afficher ensuite. Si vous avez des mises à jour, un blog fera l'affaire. Je n'encourage pas cela, mais si vous le devez vraiment (je ne vois pas pourquoi), vous pouvez écrire un commentaire sous votre message et fusionner plus tard. Je vous encourage également à supprimer tous les commentaires obsolètes et à les incorporer dans une seule question transparente - il est difficile de tout lire. J'essaie de créer mon propre algorithme, différent de tout présenté, mais je ne suis pas satisfait des résultats - donc je ne poste pas de partiels à modifier - la boîte de réponse est pour les pleins.
Evil

3
@DavidJames, je comprends. Cependant, cela ne change toujours pas nos directives: veuillez ne pas apporter autant de modifications. (Si vous souhaitez proposer des améliorations au site Web, n'hésitez pas à faire un post sur notre Meta Computer Science ou sur meta.stackexchange.com le suggérant. Les développeurs ne lisent pas ce fil de commentaire.) Mais en attendant, nous travailler avec le logiciel que nous avons, et faire de nombreuses modifications est déconseillé car il remet la question au premier plan. À ce stade, vous limiter à une modification par jour peut être une bonne ligne directrice pour laquelle tirer. N'hésitez pas à utiliser des éditeurs hors ligne ou StackEdit si cela vous aide!
DW

3
Je ne vote pas votre réponse pour plusieurs raisons. 1) il est inutile longtemps et FAR trop verbeux. Vous pouvez réduire considérablement sa présentation. 2) il y a de meilleures réponses affichées, que vous choisissez d'ignorer pour des raisons que je ne connais pas. 3) poser des questions sur le manque de votes positifs est généralement un «drapeau rouge» pour moi. 4) Cela est resté constamment en première page en raison d'un nombre fou de modifications.
Nicholas Mancuso
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.