How can I implement Maxwell's distribution?

流过昼夜 提交于 2020-05-15 21:52:02

问题


I am given the following problem to solve (This text is translated from Russian. So, there may be some translation issues):

... Another method to draw from the normal distribution is to draw two independent random numbers from the uniform distribution x1, x2 ∈ [0:0, 1:0). Then apply the following transformation:

resulting in two randomly independent numbers n1, n2 from a normal distribution with zero expected value and unit variance.

To change the distribution parameters to other parameters, e.g. the expected value for and the variance to, you should multiply the result of the draw by and add, i.e.

In the equation above, N(μ, σ) is a random variable with normal distribution with expected value μ and variance σ.

According to the Maxwell distribution, each component (x, y or z) of the velocity vector v is a random variable from a normal distribution with zero expected value, and variance

where m is the mass of the molecule, T is the temperature in Kelvin, kB is Boltzmann constant.

Your task: Draw 10,000 velocity vectors for the N2 nitrogen molecule at 300K. Calculate the average length of these vectors, and therefore the average value of the speed of the nitrogen molecule, using the formula:

public class Maxwell
{
    public double N1 { get; private set; }
    public double N2 { get; private set; }
    public void Compute(Random random)
    {
        double x1 = random.NextDouble();
        double x2 = random.NextDouble();

        N1 = Math.Sqrt(-2 * Math.Log(x1)) * Math.Cos(2 * Math.PI * x2);
        N2 = Math.Sqrt(-2 * Math.Log(x1)) * Math.Sin(2 * Math.PI * x2);
    }
}

public class Program
{
    static void Main(string[] args)
    {
        Random r = new Random();
        Maxwell m = new Maxwell();
        m.Compute(r);

        double n1 = m.N1;
        double n2 = m.N2;

        //.....? 
    }
}

I am not understanding how to implement N(μ, σ) from n1 and n2, and how to get to vector v from there.

Can anyone help?

Edit: I have implemented it on the basis of Eric Lippert's answer:

using System;

public class CommonDistributions
{
    public static double Uniform(Random random)
    {
        return random.NextDouble();
    }

    static double Gaussian(Random random)
    {
        return Math.Sqrt(-2 * Math.Log(Uniform(random))) * Math.Cos(2 * Math.PI * Uniform(random));
    }
    public static double Gaussian(Random random, double mu, double sigma)
    {
        return sigma * Gaussian(random) + mu;
    }
}

public class MaxwellBolzman
{
    static double KB = 1.38064852e-23;

    static double MaxwellVariance(double mass, double temperature)
    {
        return Math.Sqrt(KB * temperature / mass);
    }

    static double MaxwellComponent(Random random, double mass, double temperature)
    {
        double mu = 0.0;
        double sigma = MaxwellVariance(mass, temperature);

        return CommonDistributions.Gaussian(random, mu, sigma);
    }
    public static double Maxwell(Random random, double mass, double temperature)
    {
        double one = MaxwellComponent(random, mass, temperature);
        double two = MaxwellComponent(random, mass, temperature);
        double thr = MaxwellComponent(random, mass, temperature);

        return Math.Sqrt(one * one + two * two + thr * thr);
    }
}

public static class MainClass
{
    public static void Main(String[] args)
    {
        Random random = new Random();

        const int N = 10000;
        const int T = 300;//300K
        const double mass = 28.02;//28.02 g/mol

        double sum = 0.0;

        for (int i = 1; i < N; i++)
        {
            sum = sum + MaxwellBolzman.Maxwell(random, mass, T);
        }

        Console.WriteLine($"Maxwell-Boltzman = {sum/N}");

        string str = string.Empty;
    }
}

I am not sure about the values of temperature and the mass of Nitrogen 2.

It would be nice if someone can comment on the code.


回答1:


The thing to do in this situation is to think "suppose I had a magic box that answered a specific question that I posed to it; what would the inputs and outputs of that box be?" and the write a method that implements that box.

Start with the easiest box. No inputs, output is a uniformly distributed number between zero and one:

static Random random = new Random();
static double Uniform() => random.NextDouble();

OK, now we have a new tool in our toolbox. What's our next magic box? No inputs, output is a normally distributed number with mean zero and standard deviation one:

static double StandardNormal() =>
  Sqrt(-2 * Log(Uniform())) * Cos(2 * PI * Uniform());

And we have another tool. What can we build with it? Inputs: mean and standard deviation, output, normally distributed number with that mean and standard deviation:

static double Normal(double mean, double sigma) =>
  sigma * StandardNormal() + mean;

OK, now what do we need? Variance as a function of mass and temperature:

static double KB = 1.38064852e-23;
static double MaxwellVariance(double mass, double temperature) => 
  Sqrt(KB * temperature / mass);

Super, we are moving right along. Now what do we need? Input is mass and temperature, output is a single random Maxwell velocity component:

static double MaxwellComponent(double mass, double temperature) =>
  Normal(0.0, MaxwellVariance(mass, temperature));

Now what do we need? A type to represent a vector:

struct Vector
{
  public double X { get; }
  public double Y { get; }
  public double Z { get; }
  public Vector(double x, double y, double z)
  {
    this.X = x;
    this.Y = y;
    thiz.Z = z;
  }
}

What do we need next? A random vector:

static Vector MaxwellVector(double mass, double temperature) =>
  ...

can you take it from here? What will you need next? Again, keep breaking it down into one-liners. Don't get fancy. There's no prize for writing long code that you don't understand.

The technique here is divide and conquer. With these problems you can almost always write a method of less than five lines of code that computes just one thing. So do that; compute just one thing every time, and then you have a new tool in your toolkit for computing the next thing. And moreover, you have a collection of methods each of which is (1) obviously correct because it is only a single line of code, and (2) testable! Write a test suite!


UPDATE: The question has been updated to implement some of these ideas, and it looks pretty good. There's a follow-up question about the temperature and mass.

The temperature looks fine; 300K. But the mass is completely wrong. The instructions say to use the mass of one molecule but you've input the mass of one mol of molecules.

Remember, a "mol" is like a "pair" or a "dozen". A pair is two things, a dozen is twelve things, a mol is around 600000000000000000000000 things. Obviously a molecule of N2 does not weigh 28 grams. Rather, 600000000000000000000000 molecules of N2 weighs 28 grams.

Remember also that the metric units of mass and volume were chosen entirely arbitrarily. If you take the circumference of the Earth, divide it by 4 billion, make a cubic box with sides that length, and fill it with water, that's a mass of one gram.

We have chosen the value associated with "mol" because it has the property that a mol of the same kind of molecule has a mass equal to the atomic weight of the molecule in grams. So eighteen of those little boxes have one mol of water molecules. Using molar mass is just a convenience because it makes the numbers more "reasonably sized" for our purposes; normally we are used to thinking about some number of grams of water, not some number of molecules of water, but your problem concerns only ten thousand molecules, not ten thousand grams. So what you want to do is divide the mass of one mol by the number of things in a mol, and that gives you the mass of one molecule in grams.

The next thing to do is to do a unit analysis to determine whether the mass needs to be in grams or kilograms! We have for KB the value 1.38E-23, which Wikipedia helpfully notes has units of Joules per Kelvin. How are we using that? We're taking the square root of KB*T/M. What are the units the square root needs to be? It is standard deviation of speed which has units of meters per second, so we need KB*T/M to have units of meters squared per seconds squared.

  • KB is Joules per Kelvin; T is Kelvin, so KB * T has units of Joules.
  • Joules have units of kilograms times meters squared per seconds squared.
  • Therefore to get meters squared per seconds squared we need to divide by kilograms, not grams.

So what you need is grams per mol divided by molecules per mol to get grams per molecule, and then convert that to kilograms per molecule.

Make sense? Get in the habit of doing a unit analysis for every problem. That caught so many of my mistakes when I was a physics student back in the dark ages.

Aside: Speaking of unit analysis, something to be cautious of: the excerpt from your text calls the standard deviation the variance, but the standard deviation is actually defined as the square root of the variance. This usage is extremely common and you're expected to infer from context whether "variance" means "really variance" or, in this case, standard deviation.

That is, the text should say "N(μ, σ) is a random variable with normal distribution with expected value μ and variance σ2." Or it should say "N(μ, σ) is a random variable with normal distribution with expected value μ and standard deviation σ." Be on the lookout for that and read defensively.

Another aside: You may have noticed that the way we represent distributions is very "clunky". It feels like you have to do a lot of work to represent something fairly simple. My current research is in probabilistic languages which make this sort of work very straightforward. In a probabilistic language we would represent your workflow something like this:

IDistribution<double> Speed(double mass, double temp)
{
  IDistribution<double> c = 
    Normal.Distribution(0.0, MaxwellVariance(mass, temp))
  double x = sample c;
  double y = sample c;
  double z = sample c;
  return Sqrt(x*x + y*y + z*z);
}
...
double mean = Speed(mass, temp).Mean(10000);

(If this looks like an async method with Task<T> replaced by IDistribution<T> and await replaced by sample, that's because it is; both asynchronous and probabilistic workflows can be implemented as coroutines.)

If the subject of probabilistic languages interests you, I have a gentle but long introduction that starts here: https://ericlippert.com/2019/01/31/fixing-random-part-1/



来源:https://stackoverflow.com/questions/61218836/how-can-i-implement-maxwells-distribution

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!