KristerV
I’m trying to generate a list of numbers, where each number is a percentage smaller than the previous. Starting with 1. In the end I’ll need 10 (or whatever amount) of items from the end of it.
The math formula I came up with for a single number looks pretty simple:
Xn+1 = Xn - Xn * S
S = 0.008
X0 = 1
But the function I came up with is totally bonkers. While this works, it’s big and slow. My intuition says it’s possible to just make a calculation for each number, but I suck at math and don’t even know what to search for.
Ugly 20 lines of Enum.reduce
@doc """
Make a grid of prices around initial price.
iex> price_grid("0.123", "0.001", 2, "0.001")
[
%{price: D.new("0.125"), side: "ask"},
%{price: D.new("0.124"), side: "ask"},
%{price: D.new("0.122"), side: "bid"},
%{price: D.new("0.121"), side: "bid"}
]
"""
def price_grid(price, spread, levels, tick_size) do
starting_point = "1"
levels_total = levels * 2 + 1
min_mid_price = D.div(spread, "2") |> D.mult(price) |> then(&D.sub(price, &1))
max_iterations = 100_000
Enum.reduce_while(0..max_iterations, {starting_point, []}, fn i, {curr_price, grid} ->
new_price = D.sub(curr_price, D.mult(curr_price, spread))
grid = Enum.take(grid ++ [new_price], -levels_total)
mid_price = Enum.slice(grid, levels, 1) |> List.first()
if i == max_iterations or (not is_nil(mid_price) and D.lt?(mid_price, min_mid_price)) do
IO.inspect(i, label: "HALT")
grid = Enum.map(grid, &round_tick(&1, tick_size))
{:halt, grid}
else
{:cont, {new_price, grid}}
end
end)
end
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
ruslandoga
Not sure if that’s what you are looking for, but I think the formula for
x_nisx_n = x_0 * (1 - S)^n:And ignoring math, generating the numbers could be done like this:
Again, not sure if this is indeed what you need. I don’t quite understand the code in your snippet …
freevova
Hey @KristerV .
You can use Stream.iterate/2 here.
LostKobrakai
This would certainly benefit from a better explanation of your goal(s) with this.
Though the code you did post can (for the most part) be written with higher level primitives.
KristerV
awesome @ruslandoga that’s it! does it have a name? very useful.
@freevova and @LostKobrakai the Stream.iterate i wasn’t aware of, will provide useful for sure.
LostKobrakai
https://www.thoughtco.com/calculate-decay-factor-2312218
spapas
Hello ! I tried the solution
and was expecting to blow the stack since it isn’t tail optimized when run with a big count (like 100000); however it worked fine! Can anybody explain to me why it works ? How it’s even possible?
i.e this works fine:
al2o3cr
There’s some discussion about this in the Erlang Efficiency Guide; short short version tail recursion isn’t necessarily faster or more memory-efficient.
spapas
Yes but since this is not a tail-recursive function should it blow the stack ? Is the erlang stack unlimited ? I tried it with really big numbers and I have a memory consumption of like 4 GB for that program but it seems to be working without breaking (!)
al2o3cr
The BEAM doesn’t pass everything via the stack, it has registers as well. The BEAM book has a good introduction to the situation.
You can go down this rabbithole further with compiler options - add
@compile :Sinside theSeriesmodule definition and then runelixircon the file. You’ll get an error message:but you’ll ALSO get a file with
.Son the end of the name with a BEAM assembly dump in it!Here’s what
Series.generatecompiled to with Elixir 1.13 / OTP 24:{label, 12}is the main entry point.It checks the
whenclause on the third argument (arguments are passed inxregisters starting with{x, 0}) and bails out to label 13 for the base case: put[](Erlang spells itnil) into register{x, 0}(the register used for the return value) and return.The next two instructions do the actual computation, putting
x * sinto register{x,3}andcount - 1into{x,2}. Note that the compiler has determined that the original value ofcountthat was passed in via{x,2}is no longer “live” (visible to code) so it can reuse the register to storecount - 1.allocatecreates space on the stack for the return address and one saved register - the stack is read via theyregisters, again starting with{y,0}.The next two instructions shuffle registers around:
{x,0}is saved in{y,0}(preserving the original value ofx) and thenx * sis copied from{x,3}into{x,0}.That lines everything up for a new call to
generate: the three needed arguments are in{x,0}through{x,2}(sis unchanged from one iteration to the next).test_heap2verifies there’s enough space on the heap for the new cons cell we’re about to construct (and triggers GC if needed).put_listcombinesx(from{y,0}) and the return value fromgenerate(in{x,0}) into a new cons cell and puts a pointer to it into{x,0}deallocatecleans up the two stack slots, and thengeneratereturns.Every time
generaterecurses, it grows the stack by two slots (the return address and{y,0}).Every time it returns from a recursion, it shrinks the stack by two slots and creates a cons cell which takes two slots on the heap.
Overall, the body-recursive function allocates a maximum of
2 * count + 2slots - first all from the stack until the recursion hits bottom, and then trading stack for heap until completion.What about the tail-recursive version?
This is a fairly standard body → tail recursion conversion:
and the assembly shows the tail-recursion we wanted to see:
call_onlyis the indication that the compiler has successfully detected tail-recursion.Some other important differences:
allocateinstructions at all, so no stack usageNo stack usage, that’s good, right? There’s a tradeoff: in principle, constructing the reversed list at the end doesn’t need to take
2 * countmemory (all that “reverse a linked list in-place” drilling is actually useful for once!) but it does take time.