下の図のグラフに似たグラフが欲しいです。cos/sinに似たグラフを実現したいのですが、少しランダムです。グラフは、制限を超えたり (100) も下回ったり (0) することもありません。ランダム性がなければ、次のような関数を書くことができます:
f(x)=cos(x)*50+50
任意の言語での実装または簡単な説明を探しています。
いくつかの sin/cos をランダムな係数と周期で単純に合計できます。
実装例:
internal struct SineWave
{
internal readonly double Amplitude;
internal readonly double OrdinaryFrequency;
internal readonly double AngularFrequency;
internal readonly double Phase;
internal readonly double ShiftY;
internal SineWave(double amplitude, double ordinaryFrequency, double phase, double shiftY)
{
Amplitude = amplitude;
OrdinaryFrequency = ordinaryFrequency;
AngularFrequency = 2 * Math.PI * ordinaryFrequency;
Phase = phase;
ShiftY = shiftY;
}
}
public class RandomCurve
{
private SineWave[] m_sineWaves;
public RandomCurve(int components, double minY, double maxY, double flatness)
{
m_sineWaves = new SineWave[components];
double totalPeakToPeakAmplitude = maxY - minY;
double averagePeakToPeakAmplitude = totalPeakToPeakAmplitude / components;
int prime = 1;
Random r = new Random();
for (int i = 0; i < components; i++)
{
// from 0.5 to 1.5 of averagePeakToPeakAmplitude
double peakToPeakAmplitude = averagePeakToPeakAmplitude * (r.NextDouble() + 0.5d);
// peak amplitude is a hald of peak-to-peak amplitude
double amplitude = peakToPeakAmplitude / 2d;
// period should be a multiple of the prime number to avoid regularities
prime = Utils.GetNextPrime(prime);
double period = flatness * prime;
// ordinary frequency is reciprocal of period
double ordinaryFrequency = 1d / period;
// random phase
double phase = 2 * Math.PI * (r.NextDouble() + 0.5d);
// shiftY is the same as amplitude
double shiftY = amplitude;
m_sineWaves[i] =
new SineWave(amplitude, ordinaryFrequency, phase, shiftY);
}
}
public double GetY(double x)
{
double y = 0;
for (int i = 0; i < m_sineWaves.Length; i++)
y += m_sineWaves[i].Amplitude * Math.Sin(m_sineWaves[i].AngularFrequency * x + m_sineWaves[i].Phase) + m_sineWaves[i].ShiftY;
return y;
}
}
internal static class Utils
{
internal static int GetNextPrime(int i)
{
int nextPrime = i + 1;
for (; !IsPrime(nextPrime); nextPrime++) ;
return nextPrime;
}
private static bool IsPrime(int number)
{
if (number == 1) return false;
if (number == 2) return true;
for (int i = 2; i < number; ++i)
if (number % i == 0) return false;
return true;
}
}
これは C# で書かれたコードで、ランダムな入力値でさらにランダム化できます。値を含む出力を提供して、問題がないかどうかを確認します。Amplite は余弦値と正弦値に対して変更できます。最後にオフセットが追加され (最小値は常に 0)、最大値が 100 になるようにスケーリングが行われます。ご覧のとおり、ノイズを追加できます (図 3、図 4)。
terr1: RandomTerrarain(1000, 4, 1, 10, 5, 0); 図1
terr2: RandomTerrarain(1000, 2, 3, -10, 5, 0); 図 2
お役に立てれば!
private static Random rnd = new Random();
private double[] RandomTerrarain(int length, int sinuses, int cosinuses, double amplsin, double amplcos, double noise)
{
if (length <= 0)
throw new ArgumentOutOfRangeException("length", length, "Length must be greater than zero!");
double[] returnValues = new double[length];
for (int i = 0; i < length; i++)
{
// sin
for (int sin = 1; sin <= sinuses; sin++)
{
returnValues[i] += amplsin * Math.Sin((2 * sin * i * Math.PI) / (double)length);
}
// cos
for (int cos = 1; cos <= cosinuses; cos++)
{
returnValues[i] += amplcos * Math.Cos((2 * cos * i * Math.PI) / (double)length);
}
// noise
returnValues[i] += (noise * rnd.NextDouble()) - (noise * rnd.NextDouble());
}
// give offset so it be higher than 0
double ofs = returnValues.Min();
if (ofs < 0)
{
ofs *= -1;
for (int i = 0; i < length; i++)
{
returnValues[i] += ofs;
}
}
// resize to be fit in 100
double max = returnValues.Max();
if (max >= 100)
{
double scaler = max / 100.0;
for (int i = 0; i < length; i++)
{
returnValues[i] /= scaler;
}
}
return returnValues;
}