2015-12-04 9 views
0

Я пытаюсь воссоздать реализацию Java арифметического кодирования, как описано в этой ссылке, в разделе «Арифметика Coding: как это работает»: linkАрифметика Java Coding - Поиск символов Диапазоны

Я в точке, где отдельным символам необходимо назначить диапазон вдоль линии вероятности. Однако у меня возникают некоторые проблемы при создании правильных диапазонов. В моем коде, показанном ниже, это выполняется setRanges(). Ожидаемый результат должен быть таким:

Character Ranges - 

      0.0 - 0.09999999999999999 
A   0.1 - 0.19999999999999999 
B   0.2 - 0.29999999999999999 
E   0.3 - 0.39999999999999999 
G   0.4 - 0.49999999999999999 
I   0.5 - 0.59999999999999999 
L   0.6 - 0.79999999999999999 
S   0.8 - 0.89999999999999999 
T   0.9 - 0.99999999999999999 

Мой выходной ток это:

диапазонов символов -

  0.0 - 0.09999999999999999 
A   0.1 - 0.2 
B   0.2 - 0.30000000000000004 
E   0.30000000000000004 - 0.4 
G   0.4 - 0.5 
I   0.5 - 0.6 
L   0.6 - 0.8 
S   0.8 - 0.9 
T   0.9 - 1.0 

Я не уверен, есть ли лучший способ кодировать мой метод setRanges (), или это просто результат ошибок округления.

Вот Range класс, который просто содержит низкое и высокое значение с плавающей точкой:

public class Range { 

    private double low, high; 

    public Range(double low, double high) { 
     this.low = low; 
     this.high = high; 
    } 

    public String toString() { 
     return low + " - " + high; 
    } 

} 

Метод:

import java.util.TreeMap; 

    public static TreeMap<Character, Range> setRanges(TreeMap<Character, Double> treeMap) { 
     TreeMap<Character, Range> rangeMap = new TreeMap<>(); 
     double currentValue; 
     double previousValue = 0; 
     double runningTotal = 0; 

     for(Character key : treeMap.keySet()) { 
      currentValue = treeMap.get(key) + runningTotal; 
      rangeMap.put(key, new Range(previousValue, currentValue - 0.00000000000000001)); 
      previousValue = currentValue; 
      runningTotal += treeMap.get(key); 
     } 
     return rangeMap; 
    } 

} 
+0

это абсолютно необходимо изменить код из-за 0.000000000001? – nafas

+0

Меня попросили реализовать это для школьной работы, в заявлении сайта также отмечается, что персонаж «владеет» всем до, но не включая большее число. Таким образом, буква «Т» на самом деле имеет диапазон 0,90-0,9999 ... ». – user3371750

+1

Как вы можете также прочитать в этом учебнике, никто никогда не реализует арифметическое кодирование с десятичными знаками. У этого есть все эти сумасшедшие прецизионные ловушки, которые трудно позаботиться, и это становится очень медленным, когда объем производства растет. – harold

ответ

2

Я думаю, вам нужно использовать BigDecimal для этой точности. С 128 или без функции руинга. Смотри ниже:

double first = 1d; 
double second = 0.00000000000000001d; 

System.out.println("Db --> " + (first - second)); 

BigDecimal firstBd = new BigDecimal(first); 
BigDecimal secondBd = new BigDecimal(second); 
BigDecimal resultBd = firstBd.subtract(secondBd); 

System.out.println("32 --> " + resultBd.round(MathContext.DECIMAL32)); 
System.out.println("64 --> " + resultBd.round(MathContext.DECIMAL64)); 
System.out.println("128--> " + resultBd.round(MathContext.DECIMAL128)); 
System.out.println("Unl--> " + resultBd); 

Выход:

Дб -> 1,0
32 -> 1,000000
64 -> 1,000000000000000
128 -> 0.9999999999999999899999999999999993
Unl -> 0.9999999999999999899999999999999992845757594537807549147194381507675227382936355979836662299931049346923828125

+0

Уровень точности произволен, рабочие состояния «Тип данных с плавающей точкой с двойной точностью» можно использовать для хранения кодированного значения, но имейте в виду, что в соответствии со стандартом IEEE 745 52-битная мантисса будет обеспечивать только до 16 цифр точности. Поэтому длина строки, подлежащей кодированию, должна быть соответствующим образом ограничена ». Я просто использовал отрицательный 0,000000000001, чтобы получить наименьшую цифру, которую двойной мог бы дать ниже всего числа. – user3371750

+0

Я проверил и посмотрю, с двойным 0.0000000000000001d - это предел, который вы можете урезать с 1. Результат будет: Db -> 0.9999999999999999 Однако когда вы добавляете еще один «0» после точки, результат становится: Db -> 1.0 Итак, если первый предел, достаточный для вас, вы можете игнорировать решение BigDecimal и продолжать использовать double. Это может варьироваться в зависимости от установки 32-64 бит Java. – hsnkhrmn

+0

Да, похоже, это так, однако, когда я использую это в первом диапазоне, например, я все равно получаю 0.0 - 0.09999999999999991, что, я полагаю, является лишь неизбежной ошибкой округления. Спасибо за вашу помощь – user3371750

Смежные вопросы