Давайте предположим, что ваши формулы (уравнения) не являются циклическими, так как в противном случае вы не можете «просто» оценить их. Если у вас есть векторизованные уравнения, такие как A = B + C, где A, B и C являются массивами, давайте концептуально разделим их на уравнения для компонентов, так что если размер массива равен 5, это уравнение будет разбито на
a1 = b1 + c1
a2 = b2 + c2
...
a5 = b5 + c5
Теперь, предполагая это, у вас есть большой набор уравнений для простых величин (целых, рациональных или что-то еще).
Если у вас есть два уравнения E и F, скажем, что F зависит от E, если правая часть F упоминает левую часть E, например
E: a = b + c
F: q = 2*a + y
Теперь, чтобы перейти к тому, как рассчитать это, вы всегда можете использовать рандомизированную итерацию, чтобы решить это (это всего лишь промежуточный шаг в объяснении), следуя этому алгоритму:
1 while (there is at least one equation which has not been computed yet)
2 select one such pending equation E so that:
3 for every equation D such that E depends_on D:
4 D has been already computed
5 calculate the left-hand side of E
Этот процесс завершается с правильным ответом, независимо от того, как вы делаете выбор в строке // 2. Теперь круто то, что он также легко распараллеливается. Вы можете запустить его в произвольном количестве потоков! То, что вам нужно, это безопасная для параллелизма очередь, которая содержит те уравнения, чьи предпосылки (от которых зависят эти уравнения) были вычислены, но которые еще не были вычислены сами. Каждый поток выдает (потокобезопасно) одно уравнение из этой очереди за раз, вычисляет ответ, а затем проверяет, существуют ли теперь новые уравнения, чтобы были вычислены все их предпосылки, а затем добавляет эти уравнения (потокобезопасно) в очередь на работу. Готово.