Java не детерминирован?

Я написал небольшое симулятор хищника-жертвы на Java. Даже если правила довольно сложны и заканчиваются хаотичной системой, используемые методы просты:

  • арифметика и решения по основным типам данных
  • нет внешних библиотек
  • внешние системы не включены
  • no concurrency происходит
  • отсутствие текущего времени или даты

Поэтому я думал, что при инициализации системы с одинаковыми параметрами он должен выводить одинаковые результаты, но это не так, и мне интересно, почему.

Некоторые мысли по этому поводу: Мое приложение использует Random s, но для этого теста я инициализирую их все с заданным значением, поэтому, в моем понимании, они должны создавать для каждого запуска те же самые выходы в том же порядке.

Я повторяю через Set s, и я знаю, что порядок a Set итерирован, не определен. Но я не вижу причин, почему Set, который заполняется в том же порядке с одинаковыми значениями, должен вести себя по-разному в нескольких прогонах. Это?

Я использую много float s. Типы данных, где 1 + 1 = 1.9999999999725, всегда подозревают меня, но я даже если их поведение странно для меня, оно должно быть всегда одинаковым. Не правда ли?

Сбор мусора не детерминирован, но пока я не полагаюсь на деструкторов, я должен быть в безопасности.

Как указано выше, в зависимости от фактического времени использования нет concurrency и нет типов данных.

Я не могу воспроизвести это поведение в простом примере. Но, проходя мой код, я не вижу ничего, что могло бы быть непредсказуемым. Так не ошибаются ли какие-либо из моих предположений? Любые идеи, что я мог бы потерять?

Здесь проверите мои предположения:

public static void main(String[] args) {
    Random r = new Random(1);
    Set<Float> s = new HashSet<Float>();
    for (int i = 0; i < 1000000; i++) {
        s.add(r.nextFloat());
    }

    float ret = 1;
    int cnt = 0;
    for (Float f : s) {
        float multiply = 0.3f;
        if (cnt++ % 2 == 0) {
            multiply = 0.7f;
        }
        float f2 = (f * multiply);
        ret += f2;
    }

    System.out.println(ret);
}

Это всегда для меня 242455.25.

Ответ 1

Вы можете написать детерминированную программу на Java. Вам просто нужно устранить возможные источники недетерминированности.

Трудно понять, что может быть причиной недетерминированности, не видя вашего фактического кода и конкретных доказательств этого детерминизма.

Существует множество методов библиотеки, которые потенциально могут быть источниками недетерминированного поведения... в зависимости от того, как вы их используете.

Например, значение, возвращаемое Object.hashcode() (при первом вызове экземпляра), не является детерминированным. И это просачивается в любую библиотеку, которая использует хеширование. Это может определенно повлиять на порядок, в котором элементы HashSet или HashMap возвращаются, когда вы их итерации... если класс элемента не переопределяет hashcode().

Генераторы случайных чисел могут быть или не быть детерминированными. Если они являются псевдослучайными, и они инициализируются фиксированными семенами, то последовательность чисел, создаваемая каждой из них, будет детерминированной.

Арифметика с плавающей точкой должна быть детерминированной. Для любого (фиксированного) набора входов для арифметического выражения результат всегда должен быть одинаковым. (Я не уверен, что детерминизм арифметики с плавающей запятой гарантирован JLS, но было бы странно странно, если бы это произошло на практике. Как и в... вы работаете на сломанном оборудовании.)


FOLLOWUP... на strictfp и недетерминированности.

В соответствии с JLS 15.4:

"В выражении, которое не является FP-строгим, предоставляется некоторая свобода реализации для использования расширенного диапазона экспонентов для представления промежуточных результатов, а чистый эффект, грубо говоря, заключается в том, что расчет может привести к" правильному ответу ", в ситуациях, когда исключительное использование установленного значения с плавающей запятой или двойного значения может привести к переполнению или недопущению."

Это точно не говорит о том, насколько "свобода" реализации имеет строгие выражения, отличные от FP. Однако я бы подумал, что эта свобода не будет распространяться на возможность детерминированного поведения. Я бы подумал, что компилятор JIT на конкретной платформе всегда будет генерировать эквивалентный собственный код для одного и того же выражения, и этот код будет детерминированным. (Я не вижу никакой причины для недетерминированности... если только аппаратное обеспечение не имеет детерминированной точки с плавающей точкой.) Другим возможным источником недетерминизма может быть то, что поведение JIT-компилируемого и интерпретируемого кода может быть другим. Но, честно говоря, я думаю, что это было бы "орехом", чтобы это произошло... и я думаю, мы бы об этом слышали.

Таким образом, хотя оценка не-FP-строгой оценки может быть недетерминированной в теории, я думаю, что мы должны упустить это... если нет четких доказательств того, что это происходит на практике.

(Обратите внимание, что я говорю о реальном недетерминизме, а не о различиях в платформе.)

Ответ 2

Я выполняю итерацию наборы, и я знаю, что порядок, заданный итерацией Set, не определен. Но я не вижу причин, почему набор, который заполняется в том же порядке с одинаковыми значениями, должен вести себя differnet в нескольких прогонах. Это?

Он может. Реализация может использовать, например, местоположение объекта в памяти в качестве ключа в основной хеш-таблице. Это может варьироваться в зависимости от того, когда выполняется сбор мусора.