{"id":178,"date":"2009-08-02T19:29:27","date_gmt":"2009-08-02T18:29:27","guid":{"rendered":"http:\/\/mehm.net\/blog\/?p=178"},"modified":"2009-10-31T14:46:21","modified_gmt":"2009-10-31T13:46:21","slug":"persona-3-fes-calculator-algorithmic-optimization-part-12","status":"publish","type":"post","link":"https:\/\/mehm.net\/blog\/?p=178","title":{"rendered":"Persona 3 FES Calculator &#8211; Algorithmic Optimization (1)"},"content":{"rendered":"<p>As noted earlier, in this post I want to have a look at how the general algorithm I used in the calculator can be optimized without applying any language-specific optimizations.<\/p>\n<p><strong>Normal Spread Fusions<br \/>\n<\/strong><\/p>\n<p>In the case of regular fusions, when we simply try to fuse each Persona on the list with each other Persona on the list, we will have to try out <img src='https:\/\/s0.wp.com\/latex.php?latex=n%5E%7B2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='n^{2}' title='n^{2}' class='latex' \/> fusions. However, there are many unnecessary fusions in there. First, we don&#8217;t want to try to fuse the same Persona with itself, since this is invalid. This means we are not allowing all sequences of two elements <img src='https:\/\/s0.wp.com\/latex.php?latex=%28p_%7B1%7D%2C+p_%7B2%7D%29&#038;bg=T&#038;fg=000000&#038;s=0' alt='(p_{1}, p_{2})' title='(p_{1}, p_{2})' class='latex' \/>, but we restrict us to all sequences where <img src='https:\/\/s0.wp.com\/latex.php?latex=p_%7B1%7D+%5Cneq+p_%7B2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='p_{1} \\neq p_{2}' title='p_{1} \\neq p_{2}' class='latex' \/>. This results in a number of slightly fewer fusion tries, namely <img src='https:\/\/s0.wp.com\/latex.php?latex=n%5E%7B2%7D-n&#038;bg=T&#038;fg=000000&#038;s=0' alt='n^{2}-n' title='n^{2}-n' class='latex' \/>. However, we are still trying all orders, meaning that if we tested <img src='https:\/\/s0.wp.com\/latex.php?latex=%28p_%7B1%7D%2C+p_%7B2%7D%29&#038;bg=T&#038;fg=000000&#038;s=0' alt='(p_{1}, p_{2})' title='(p_{1}, p_{2})' class='latex' \/>, we will also test <img src='https:\/\/s0.wp.com\/latex.php?latex=%28p_%7B2%7D%2C+p_%7B1%7D%29&#038;bg=T&#038;fg=000000&#038;s=0' alt='(p_{2}, p_{1})' title='(p_{2}, p_{1})' class='latex' \/>, even though this results in the same result. Therefore, what we want to test are only the subsets of size 2, for which there are of course <img src='https:\/\/s0.wp.com\/latex.php?latex=n+%5Cchoose+2&#038;bg=T&#038;fg=000000&#038;s=0' alt='n \\choose 2' title='n \\choose 2' class='latex' \/>\u00c2\u00a0 possibilites. We can reach this with the following bit of code:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nfor (int i = 0; i &lt; persona.Count; i++) {\r\n    for (int j = i + 1; j &lt; persona.Count; j++) {\r\n        Persona p1 = persona[i];\r\n        Persona p2 = persona[j];\r\n        Persona result = NormalSpreadFusion(p1, p2);\r\n    }\r\n}\r\n<\/pre>\n<p>Unfortunately, we have still quadratic complexity in the number of fusion tests, but we have managed to reduce the absolute number of comparisons quite a bit. For example, if we have about 150 Persona (which is close to the worst case of all but one Persona being in the list), the brute force approach would test 22500 fusions. With the reduced number of fusions, we are down to 11175 fusions.<\/p>\n<p><strong>Triangle Spread Fusions<\/strong><\/p>\n<p>Triangle Spread fusions work a bit different than regular spread fusions. If we want to get the result of a triangle fusion, we have to sort the Persona by their level (and their Arcana&#8217;s number in case there is a draw). Then, a regular fusion is carried out on the first (lower) two Persona, followed by another two-Persona fusion (albeit with special rules) to get the final Persona. Again, a brute-force approach would put all possible sequences with three Persona to the test and internally sort them. This results in a cubic number of fusion tests, <img src='https:\/\/s0.wp.com\/latex.php?latex=n%5E%7B3%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='n^{3}' title='n^{3}' class='latex' \/>. The 3 Persona must be distinct, therefore we can exclude tuples such as <img src='https:\/\/s0.wp.com\/latex.php?latex=%28p_%7B1%7D%2C+p_%7B1%7D%2C+p_%7B2%7D%29&#038;bg=T&#038;fg=000000&#038;s=0' alt='(p_{1}, p_{1}, p_{2})' title='(p_{1}, p_{1}, p_{2})' class='latex' \/>. This results in <img src='https:\/\/s0.wp.com\/latex.php?latex=n+%28n-1%29+%28n-2%29&#038;bg=T&#038;fg=000000&#038;s=0' alt='n (n-1) (n-2)' title='n (n-1) (n-2)' class='latex' \/> possibilities. Again listing only unique subsets of size 3, we get <img src='https:\/\/s0.wp.com\/latex.php?latex=n+%5Cchoose+3&#038;bg=T&#038;fg=000000&#038;s=0' alt='n \\choose 3' title='n \\choose 3' class='latex' \/> possibilites, which we could achieve with an algorithm as described in the last section.<\/p>\n<p>However, we could do even better, not in terms of the number of fusion tests, but we can eleminate the internal sorting, and try to enumerate the Persona in lexicographical order. This means that for <img src='https:\/\/s0.wp.com\/latex.php?latex=%28p_%7B1%7D%2C+p_%7B2%7D%2C+p_%7B3%7D%29&#038;bg=T&#038;fg=000000&#038;s=0' alt='(p_{1}, p_{2}, p_{3})' title='(p_{1}, p_{2}, p_{3})' class='latex' \/>, <img src='https:\/\/s0.wp.com\/latex.php?latex=p_%7B1%7D+%5Cleq+p_%7B2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='p_{1} \\leq p_{2}' title='p_{1} \\leq p_{2}' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=p_%7B2%7D+%5Cleq+p_%7B3%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='p_{2} \\leq p_{3}' title='p_{2} \\leq p_{3}' class='latex' \/> hold (using the ordering by level and arcana as mentioned above). As declared, we don&#8217;t decrease the number of fusion tests, but we save ourselves the trouble of having to order the set of three Persona each time we encounter a combination, since we are sure that the set is already sorted. If we assume again 150 Persona, a brute-force implementation would check 3375000 triples for fusion results. With the optimization, we test 551300 fusions, without the need to sort the results again internally, at the cost of sorting the array before each round.<\/p>\n<p><strong>Iterations<\/strong><\/p>\n<p>One more possible optimization concerns the iterative nature of the approach of the calculator. Say we start with the set <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{1}' title='P_{1}' class='latex' \/> of size <img src='https:\/\/s0.wp.com\/latex.php?latex=k_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='k_{1}' title='k_{1}' class='latex' \/>. We test all combinations of the Persona in the set, getting the set <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{2}' title='P_{2}' class='latex' \/> with size <img src='https:\/\/s0.wp.com\/latex.php?latex=k_%7B2%7D+%3E+k_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='k_{2} &gt; k_{1}' title='k_{2} &gt; k_{1}' class='latex' \/>. If we continue in this fashion, we will be repeating a lot of the checks we already had in the smaller sets. Say the difference of <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{1}' title='P_{1}' class='latex' \/> and <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{2}' title='P_{2}' class='latex' \/> is <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B%5CDelta%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{\\Delta}' title='P_{\\Delta}' class='latex' \/>. We need to test all combinations of Persona in <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B%5CDelta%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{\\Delta}' title='P_{\\Delta}' class='latex' \/> as well as all combinations of one member of <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B%5CDelta%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{\\Delta}' title='P_{\\Delta}' class='latex' \/> and one of <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{1}' title='P_{1}' class='latex' \/> to get to set <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B3%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{3}' title='P_{3}' class='latex' \/>. This means we have to do <img src='https:\/\/s0.wp.com\/latex.php?latex=%5Cfrac%7Bk_%7B%5CDelta%7D+%28k_%7B%5CDelta%7D-1%29%7D%7B2%7D+%2B+k_%7B%5CDelta%7D+k_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='\\frac{k_{\\Delta} (k_{\\Delta}-1)}{2} + k_{\\Delta} k_{1}' title='\\frac{k_{\\Delta} (k_{\\Delta}-1)}{2} + k_{\\Delta} k_{1}' class='latex' \/> tests. Let&#8217;s say that <img src='https:\/\/s0.wp.com\/latex.php?latex=k_%7B1%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='k_{1}' title='k_{1}' class='latex' \/> is 70, <img src='https:\/\/s0.wp.com\/latex.php?latex=k_%7B2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='k_{2}' title='k_{2}' class='latex' \/> is 90, therefore <img src='https:\/\/s0.wp.com\/latex.php?latex=k_%7B%5CDelta%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='k_{\\Delta}' title='k_{\\Delta}' class='latex' \/> = 20. If using the naive approach to compute <img src='https:\/\/s0.wp.com\/latex.php?latex=P_%7B3%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='P_{3}' title='P_{3}' class='latex' \/>, we would require (for the normal spread fusions only) 4005 checks. If we use this alternate approach, we need 1590 checks.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As noted earlier, in this post I want to have a look at how the general algorithm I used in the calculator can be optimized without applying any language-specific optimizations. Normal Spread Fusions In the case of regular fusions, when we simply try to fuse each Persona on the list with each other Persona on [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,3],"tags":[14],"_links":{"self":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/178"}],"collection":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=178"}],"version-history":[{"count":44,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/178\/revisions"}],"predecessor-version":[{"id":623,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/178\/revisions\/623"}],"wp:attachment":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=178"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=178"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}