Skip to content

Instantly share code, notes, and snippets.

@casebeer
Last active March 3, 2024 21:28
Show Gist options
  • Save casebeer/3a06cd6c21cc3cd741e08ddcb5c6009a to your computer and use it in GitHub Desktop.
Save casebeer/3a06cd6c21cc3cd741e08ddcb5c6009a to your computer and use it in GitHub Desktop.
Random normal variables in Home Assistant automations/Jinja templates

Random normal variables in Home Assistant/Jinja

Via Iwrin-Hall/uniform sum distribution.

μ = n / 2, σ = sqrt(n / 12)

where n = # of summed uniform U(0, 1) random variables

Scale and shift Irwin-Hall to provide μ and σ as needed.

For the sum of n discrete U(0, N) distributions:

μ = N * n / 2

Var = n * Var(U(0, N)) = n * (N ** 2 - 1) / 12 so 

σ = sqrt(n * (N ** 2 - 1) / 12)

where n = # of summed uniform U(0, N) random variables

If we sum 12 discrete U(-N / 2, N / 2) distributions, we get:

μ = 0

σ ≈ N

This distribution can then be scaled to set desired granularity/resolution and then shifted to set desired μ.

Note that since we can only generate random integers in Jinja, we want to avoid losing granularity/resolution by allowing 12 * N to be too small; e.g. if we want σ = 30 seconds, we will set N = 30, 30 * 12 = 360, so our summed R.V. will be strictly in [-180, 180].

There may also be an issue with negative values.

In Jinja filters:

{% macro normalRv(mu, sigma) -%}
{{ (([ range(-sigma // 2, sigma // 2) ] * 12) | map('random') | sum) + mu }}
{%- endmacro %}

{% macro normalPositiveRv(mu, sigma) -%}
{{ (1, (([ range(-sigma // 2, sigma // 2) ] * 12) | map('random') | sum) + mu) | max }}
{%- endmacro %}

Strictly positive R.V. centered at 3600 s = 1 hour with a standard

deviation of 1800 s = half an hour:

{{ (3600 + (([ range(-900, 900) ] * 12) | map('random') | sum), 1) | max }}
  • 68% will be in [half hour, 1.5 hours]

  • 95% will be in [0, 2 hours]

  • 99%+ will be in [0, 2.5 hours]

Strictly positive R.V. centered at 1200 s = 20 mins with a standard

deviation of 600 s = 10 minutes:

{{ (1200 + (([ range(-300, 300) ] * 12) | map('random') | sum), 1) | max }}
  • 68% will be in [10 mins, half hour]

  • 95% will be in (0, 40 mins]

  • 99%+ will be in (0, 50 mins]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment