{"id":211,"date":"2020-07-01T00:23:23","date_gmt":"2020-07-01T00:23:23","guid":{"rendered":"http:\/\/96.126.106.214\/?p=211"},"modified":"2023-11-26T01:01:31","modified_gmt":"2023-11-26T01:01:31","slug":"thinking-about-algorithmic-problems","status":"publish","type":"post","link":"https:\/\/codingismycraft.blog\/index.php\/2020\/07\/01\/thinking-about-algorithmic-problems\/","title":{"rendered":"Thinking about Algorithmic problems"},"content":{"rendered":"<p>In this posting we will discuss <strong>algorithmic thinking<\/strong> and try to propose a way of thinking that might become helpful when trying to solve a problem that does not have an obvious solution and some level of <strong>improvisation<\/strong> is required.<\/p>\n<p>Based on my experience the three most important qualities of a programmer can be summarized as follows:<\/p>\n<ul>\n<li>Algorithmic thinking.<\/li>\n<li>Code design.<\/li>\n<li>Mastery of the language of choice.<\/li>\n<\/ul>\n<p>The ability to solve problems that require the <strong>invention<\/strong> of a new algorithm combined with the skill of recognizing cases that can be solved directly by a \u201cstandard\u201d one is one of the strongest signs of programming expertise and talent.<\/p>\n<p>Although it is true that the need to create a brand new algorithm does not appear often in our days, it is also equally true that a programmer who has the ability to successfully <strong>combine analytic and synthetic thinking<\/strong> and efficiently solve a multi-dimensional problem will probably be proven to be more effective than somebody who does not.<\/p>\n<p>Out of the three types of skills that I am mentioning above the one that requires the most <strong>programming talent<\/strong> is without a doubt the <strong>Algorithmic thinking<\/strong>; the most successful software companies are convinced about the the truth of this statement and this is why algorithms lie in the core of their interviewing processes.<\/p>\n<p>Although talent is definitely required in most cases it is not enough when facing a new problem; we can certainly be assisted by some \u201cmethodical\u201d steps that can serve as the blueprint for the creation of a solution and discussing them is the topic of this posting.<\/p>\n<p>A summary of these \u201cmethodical\u201d steps that I am usually following whenever I have to deal with an original problem is the following:<\/p>\n<ul>\n<li>Articulate the problem with clarity and try to understand niche cases.<\/li>\n<li>Creating several sets of testing input \u2013 output<\/li>\n<li>Code at least one brutal force solution<\/li>\n<li>Study the problem trying to understand it in depth.<\/li>\n<li>Decide if an existing algorithm can solve it directly or a special \u201ctrick\u201d is needed;<\/li>\n<li>Can one of the standard methodologies like graph, recursion, dynamic programming or data structures can be used?<\/li>\n<\/ul>\n<p>I will now pick a simple algorithmic problem and try to explain the thought process to solve it.<\/p>\n<h2>The problem<\/h2>\n<p>This is a well known problem that can be found in several websites that specialize on preparing candidates for programming positions.<br \/>\nAssume a water tank which has multiple parallel movable divisors placed in different locations as can be seen in the following picture:<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1579\" src=\"\/wp-content\/uploads\/2017\/04\/tank.gif\" alt=\"tank\" width=\"490\" height=\"330\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>To better understand the picture you have to think that its third dimension is omitted; the blue bars represent the divisors that can be removed. Each divisor is set in a specific distance that cannot change.<\/p>\n<p>The objective is to discover the two divisors that can hold the most water when others will be removed.<\/p>\n<h2>First thoughts<\/h2>\n<p>Now that we have the problem clearly defined, lets pause for a second and try to put together a trivial solution which will solve it without considering its performance.<\/p>\n<p>The fact that we are looking for a <b>pair <\/b>of divisors makes it obvious that if find all the divisor pairs and calculate the covering areas for each of them and simply select the largest one (we do not need to consider the third dimension since it will be the same for all the pairs.<\/p>\n<p>Before we do any coding and using the picture above we can create the testing data to check our brutal solution and verify it.<\/p>\n<p>Eyeballing the picture we immediately see that there are two candidate solution:<\/p>\n<p>The 1 \u2013 10 pair results to 3 X 9 = 27 while the 3 \u2013 6 pair to 7 X 3 = 21 <strong>so the expected solution will be 28.<\/strong><\/p>\n<h2>Describe the problem programmatically<\/h2>\n<p>At this point it makes sense to try to describe the problem in a programming language (for this example we will use python).<\/p>\n<p>The first thing that comes to mind is to represent the water tank programmatically. We can start by introducing a class to hold the coordinates of the upper point of each location:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport collections\r\nPoint = collections.namedtuple(&quot;Point&quot;, [&#039;x&#039;, &#039;y&#039;])\r\n<\/pre>\n<p>Now using Point we can represent the tank as follows:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ntank = [Point(1, 3), Point(3, 9), Point(6, 7), Point(10, 4)]\r\n<\/pre>\n<p>Also we can declare the function to calculate the max_area:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef get_max_area(points):\r\n    pass\r\n<\/pre>\n<p>Since we already have calculated the expected answer we can add an assertion to validate our solution:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nassert get_max_area(tank) == 28\r\n<\/pre>\n<p>At this point our code should look as follows:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport collections\r\nPoint = collections.namedtuple(&quot;Point&quot;, [&#039;x&#039;, &#039;y&#039;])\r\n\r\ntank = [Point(1, 3), Point(3, 9), Point(6, 7), Point(10, 4)]\r\n\r\ndef get_max_area(tank):\r\n    pass\r\n\r\nassert get_max_area(tank) == 28\r\n<\/pre>\n<p>If we run the program we should see the following output:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1589\" src=\"\/wp-content\/uploads\/2017\/04\/tank-1.gif\" alt=\"tank-1\" width=\"701\" height=\"113\" \/><\/p>\n<h2>A Brutal solution<\/h2>\n<p>Assuming that we are having a pair of Points (representing two divisors) its covered area can be found if we multiply its width by the lower divisor:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1591\" src=\"\/wp-content\/uploads\/2017\/04\/tank-2.gif\" alt=\"tank-2\" width=\"486\" height=\"328\" \/><\/p>\n<p>In this example the area will be covered by a 7 X 3 rectangle; 7 is the height of the lower divisor while 3 is the distance between them.<\/p>\n<p>To simplify our solution, we can define a function which will receive two divisors as Points and return the enclosed area:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef get_area(p1, p2):\r\n    width = p2.x - p1.x if p2.x &gt; p1.x else p1.x - p2.x\r\n    height = min(p2.y,  p1.y)\r\n\r\n    return width * height\r\n<\/pre>\n<p>Now we are ready to implement the brutal solution. We should simply create all possible pairs of divisors, calculate the area for each of them and return the maximum value:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport collections\r\nimport itertools\r\n\r\nPoint = collections.namedtuple(&quot;Point&quot;, [&#039;x&#039;, &#039;y&#039;])\r\n\r\ntank = [Point(1, 3), Point(3, 9), Point(6, 7), Point(10, 4)]\r\n\r\ndef get_area(p1, p2):\r\n    width = p2.x - p1.x if p2.x &gt; p1.x else p1.x - p2.x\r\n    height = min(p2.y,  p1.y)\r\n\r\n    return width * height\r\n\r\n\r\ndef get_max_area(tank):\r\n    max_area = 0\r\n    for p1, p2 in itertools.combinations(tank, 2):\r\n        max_area = max(max_area, get_area(p2, p1))\r\n    return max_area\r\n\r\n\r\nassert get_max_area(tank) == 28\r\n<\/pre>\n<p>If we run the program above we will see that it completes without an error so our thought process was correct.<\/p>\n<h2>The problem with the brutal solution<\/h2>\n<p>Our brutal solution appears to be correct, so now let&#8217;s see how it scales. Let&#8217;s go ahead and stress test it by creating a tank that consists of a large number of points and see how it behaves. To do so we need to write a function to generate random points and call our get_max_area with increasing number of divisors:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport collections\r\nimport itertools\r\nimport random\r\nimport timeit\r\nimport functools\r\n\r\nPoint = collections.namedtuple(&quot;Point&quot;, [&#039;x&#039;, &#039;y&#039;])\r\n\r\n\r\ndef get_area(p1, p2):\r\n    width = p2.x - p1.x if p2.x &gt; p1.x else p1.x - p2.x\r\n    height = min(p2.y, p1.y)\r\n\r\n    return width * height\r\n\r\n\r\ndef random_points(count, max_height):\r\n    for x in random.sample(range(1, count * 10), count):\r\n        y = random.randint(1, max_height)\r\n        yield Point(x, y)\r\n\r\n\r\ndef get_max_area(tank):\r\n    max_area = 0\r\n    for p1, p2 in itertools.combinations(tank, 2):\r\n        max_area = max(max_area, get_area(p2, p1))\r\n    return max_area\r\n\r\n\r\nfor count in [100, 1000, 5000, 10000]:\r\n    tank = list(random_points(count=count, max_height=12))\r\n    print(\r\n        &#039;count:&#039;, count, &#039;duration:&#039;,\r\n        timeit.timeit(stmt=functools.partial(get_max_area, tank), number=1)\r\n    )\r\n\r\n<\/pre>\n<p>So, running the above program resulted on the following output on my computer:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1598\" src=\"\/wp-content\/uploads\/2017\/04\/tank-3.gif\" alt=\"tank-3\" width=\"599\" height=\"132\" \/><\/p>\n<p>As you can see although our solution works fast enough when the number of divisors is low it grows very fast and when we are trying 10,000 of them it takes more than 41 seconds!<\/p>\n<p>The poor performance is caused from the fact that we are calculating the area for all the possible pairs.<\/p>\n<p>We now that the possible combination of n things by k are given by the following formula:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1601\" src=\"\/wp-content\/uploads\/2017\/04\/CodeCogsEqn.gif\" alt=\"CodeCogsEqn\" width=\"169\" height=\"44\" \/><\/p>\n<p>So assuming we have 10,000 divisors, the possible pairs will be:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1603\" src=\"\/wp-content\/uploads\/2017\/04\/combos.gif\" alt=\"combos\" width=\"161\" height=\"44\" \/><\/p>\n<p>which equals 49,995,000 or in other words it has a time complexity of O(C(n,k)) which results into an extremely slow solution.<\/p>\n<p>At this point we must revisit our problem and think if we can come up with a &#8220;smart trick&#8221; that will allow us to improve our solution.. Please pause for a while and think our problem again trying to detect if there is something that will allow us to bypass most of the calculations of the brutal solution and then move on to the explanation of the efficient solution..<\/p>\n<h2>The trick<\/h2>\n<p>Lets revisit our original example and try to go step by step to an efficient solution. This time we will start by calculating the area of the first and last point as can be seen in the following picture:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1607\" src=\"\/wp-content\/uploads\/2017\/04\/tank-4.gif\" alt=\"tank-4\" width=\"461\" height=\"308\" \/><\/p>\n<p>The area between the first and the last point is: <strong>3 X (10 &#8211; 1) = 27<\/strong>.<\/p>\n<p>Note that we use the point with the lower height and multiply it by the distance.<\/p>\n<p>The trick arises is exactly here.<\/p>\n<p>Note that for obvious the point with the lower height is impossible to result to a larger area when combined with any other point. This means that once we have calculated the first &#8211; last area in our example we can simply get rid of the first point without having to deal with it anymore.<\/p>\n<p>This is a great improvement over the brutal solution since for it each point (including the first) has to be paired with all the others. In our example this means that for the brutal solution the first point is matched against all the three others but as we can see we do not need to do so since it is impossible to find a larger area anywhere else except the last point.<\/p>\n<p>Following our observation, we keep the calculated area (27) remove the first point and continue to the next point as can be seen here:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1609\" src=\"\/wp-content\/uploads\/2017\/04\/tank-5.gif\" alt=\"tank-5\" width=\"455\" height=\"305\" \/><\/p>\n<p>Note that now the last point is lower that the first; the enclosed area is <strong>4 X (10 &#8211; 3) = 28<\/strong> which replaces the previous maximum area value (was 27). Thinking in the same way we remove the last point and now our problem looks as follows:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1611\" src=\"\/wp-content\/uploads\/2017\/04\/tank-6.gif\" alt=\"tank-6\" width=\"453\" height=\"310\" \/><\/p>\n<p>Now the enclosed area in <strong>7 X (6 -3) = 21<\/strong> which is less that the previous maximum area which remains 28 while we remove the lower Point and remain with a single point meaning that our problem is over and we have found the solution which is 28.<\/p>\n<h2>The efficient solution<\/h2>\n<p>Now that we have found the <strong>trick<\/strong> our next step is to re-write our get_max_area function replacing the brutal with the efficient logic:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ndef get_max_area(tank):\r\n    i1 = 0\r\n    i2 = len(tank) - 1\r\n    max_area = 0\r\n    while tank[i1].x &lt; tank[i2].x:\r\n        height = min(tank[i1].y, tank[i2].y)\r\n        width = tank[i2].x - tank[i1].x\r\n        max_area = max(max_area, height * width)\r\n        if tank[i1].y &gt; tank[i2].y:\r\n            i2 -= 1\r\n        else:\r\n            i1 += 1\r\n    return max_area\r\n<\/pre>\n<p>What happens here is simply expressing the steps we have followed before in python code.<\/p>\n<p>* We start with two indexes pointing in the first and last point of the tank and we initialize the max_area to 0.<\/p>\n<p>* Calculate the maximum area between the two indexes and store it if it is larger than the previous .<\/p>\n<p>* Remove the lower point.<\/p>\n<p>* Continue until the first point passes the second on the horizontal axis.<\/p>\n<p>Now are full code including the testing data and the stress test becomes as follows:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nimport collections\r\nimport random\r\nimport timeit\r\nimport functools\r\n\r\nPoint = collections.namedtuple(&quot;Point&quot;, [&#039;x&#039;, &#039;y&#039;])\r\n\r\n\r\ndef random_points(count, max_height):\r\n    for x in random.sample(range(1, count * 10), count):\r\n        y = random.randint(1, max_height)\r\n        yield Point(x, y)\r\n\r\n\r\ndef get_max_area(tank):\r\n    i1 = 0\r\n    i2 = len(tank) - 1\r\n    max_area = 0\r\n    while tank[i1].x &lt; tank[i2].x:\r\n        height = min(tank[i1].y, tank[i2].y)\r\n        width = tank[i2].x - tank[i1].x\r\n        max_area = max(max_area, height * width)\r\n        if tank[i1].y &gt; tank[i2].y:\r\n            i2 -= 1\r\n        else:\r\n            i1 += 1\r\n    return max_area\r\n\r\n\r\ntank = [Point(1, 3), Point(3, 9), Point(6, 7), Point(10, 4)]\r\nassert get_max_area(tank) == 28\r\n\r\nfor count in [100, 1000, 5000, 10000]:\r\n    tank = list(random_points(count=count, max_height=12))\r\n    print(\r\n        &#039;count:&#039;, count, &#039;duration:&#039;,\r\n        timeit.timeit(stmt=functools.partial(get_max_area, tank), number=1)\r\n    )\r\n\r\n<\/pre>\n<p>Running the program has the following output:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1616\" src=\"\/wp-content\/uploads\/2017\/04\/track-7.gif\" alt=\"track-7\" width=\"427\" height=\"118\" \/><\/p>\n<p>Note that now the 10,000 points are taking fractions of a second and compare it with the brutal solution to see how much faster this solution is.<\/p>\n<p>The complexity of the efficient solution is O(n) meaning that for 10,000 points it will run approximately 10,000 times faster!<\/p>\n<h2>Concusion<\/h2>\n<p>In this article we have discussed an overview of the methodology to solve a problem with an unknown solution and how a programmer can tackle it by following a predefined number of steps that can be applied in almost every case. Further more we have discussed a specific problem and gave an ad-hoc solution that was superior to the obvious one which we had coded firstly. Going through the same process will be helpful for the most of similar scenarios.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this posting we will discuss algorithmic thinking and try to propose a way of thinking that might become helpful when trying to solve a&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-211","post","type-post","status-publish","format-standard","hentry","category-programming"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":187,"url":"https:\/\/codingismycraft.blog\/index.php\/2020\/07\/03\/thinking-about-algorithmic-problems-ii\/","url_meta":{"origin":211,"position":0},"title":"Thinking about Algorithmic problems II","author":"john","date":"July 3, 2020","format":false,"excerpt":"Solving a programming problem that requires a custom algorithm can be broken in several steps which can be summarized in the creation of testing data, the implementation of a brutal force solution and the deeper study of the particular case which might reveal a smart trick to achieve an efficient\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":66,"url":"https:\/\/codingismycraft.blog\/index.php\/2016\/03\/10\/the-three-dimensions-of-a-programmer\/","url_meta":{"origin":211,"position":1},"title":"The three dimensions of a programmer.","author":"john","date":"March 10, 2016","format":false,"excerpt":"\u00a0 It is my impression that the vast majority of new developers, tend to underestimate the importance of design and implementation details as they struggle to prove that they can deliver a solution very quickly and please their managers with very tight deadlines. A developer eager to prove his abilities,\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":337,"url":"https:\/\/codingismycraft.blog\/index.php\/2024\/11\/06\/unveiling-the-traits-of-a-master-programmer\/","url_meta":{"origin":211,"position":2},"title":"Unveiling the Traits of a Master Programmer","author":"john","date":"November 6, 2024","format":false,"excerpt":"Programming is a craft honed through both theoretical knowledge and practical application. This skill, cultivated through countless hours of coding and problem-solving, is essential for delivering high-quality software. To achieve mastery, programmers must possess not only a strong foundation in technical knowledge but also a wealth of practical experience. This\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":204,"url":"https:\/\/codingismycraft.blog\/index.php\/2020\/07\/06\/buying-and-selling-stocks-with-maximum-profit\/","url_meta":{"origin":211,"position":3},"title":"Buying and Selling stocks with maximum profit","author":"john","date":"July 6, 2020","format":false,"excerpt":"Summary This posting continues the discussion about algorithmic thinking solving a well know problem that is known as\u00a0 Best Time to Buy and Sell Stock with maximum profit. The solution to this problem is hard and I do not believe that it makes a good interview question but it is\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":348,"url":"https:\/\/codingismycraft.blog\/index.php\/2025\/03\/19\/stop-chasing-technology-how-to-build-ai-that-solves-genuine-problems\/","url_meta":{"origin":211,"position":4},"title":"Stop Chasing Technology: Finding the user\u2019s problem is the real problem!","author":"john","date":"March 19, 2025","format":false,"excerpt":"There\u2019s a common critique in the AI community: too often, AI solutions emerge from asking, \u201cWhat can we do?\u201d rather than, \u201cWhat problem do we need to solve?\u201d Developers frequently jump on the latest algorithm or innovative model and then search for a suitable use case. This approach tends to\u2026","rel":"","context":"In &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/codingismycraft.blog\/index.php\/category\/ai\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":49,"url":"https:\/\/codingismycraft.blog\/index.php\/2014\/05\/20\/thoughts-about-software-quality\/","url_meta":{"origin":211,"position":5},"title":"Thoughts About Software Quality&#8230;","author":"john","date":"May 20, 2014","format":false,"excerpt":"This might sound a bit axiomatic but it has to be true: software that remains alive while still expanding the number of it users as time goes on, is the epitome of good software; anything else that can possibly be used as quality metric is more of an implementation detail\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/codingismycraft.blog\/index.php\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts\/211","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/comments?post=211"}],"version-history":[{"count":1,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts\/211\/revisions"}],"predecessor-version":[{"id":212,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/posts\/211\/revisions\/212"}],"wp:attachment":[{"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/media?parent=211"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/categories?post=211"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codingismycraft.blog\/index.php\/wp-json\/wp\/v2\/tags?post=211"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}