2013-07-13 2 views
12

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

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

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

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

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

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

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

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

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

Вот тест, чтобы проверить свои предположения:

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

Функция времени? – Mysticial

+1

Когда вы говорите, что вы инициализируете свои случайные данные, вы имеете в виду, что используете семя? – tjameson

+0

Это звучит интересно. Можем ли мы взглянуть на ваш код? – kol

ответ

20

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

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

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

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

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

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


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

Согласно JLS 15.4:

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

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

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

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

+1

Детерминизм арифметики с плавающей запятой не гарантируется без использования 'strictfp' - но это должно быть только между платформами. (+1) –

+0

Без 'strctfp', реализация может иногда использоваться с более высокой точностью, а иногда и без нее. –

+0

@PaulBellora - Yea. Изменчивость между платформами - это не то же самое, что и детерминизм. Если у вас есть истинный недетерминированность, тогда ваше аппаратное обеспечение fp будет ошибочным ... если не сломано. –

10

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

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

+0

@MichaelBulla - Какой JVM вы используете? – tjameson

+0

@tjameson: Я не использую какую-либо конкретную JVM. Этот вопрос о том, что может произойти, а не о том, что происходит. –

+0

Кажется, это вопрос «действительно ли», и я надеялся, что если OP сообщит нам, что он использует JVM, мы могли бы найти заметки о внедрении и посмотреть, потенциально ли это связано с реализацией. – tjameson

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