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 having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
Hello,
I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind.
However, when I launch mix phx.server, I get an error...
New
Hi everyone,
I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding.
I sta...
New
I’m working on a small exercise involving update_in/3, and I came up with this solution:
data = %{
name: "Periodic Table",
category:...
New
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication):
toke...
New
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New
Other Trending Topics
Edit: 2026 May 15 - This post is archived.
Mob is alive!!
Main docs: mob v0.7.11 — Documentation
A bit of explanation for the slightly c...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
A little off-topic, but I feel like people here have a good head on their shoulders.
I used to be quite good at making software. Was luc...
New
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
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
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex










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.