我想要类似于下图中的图形。我想实现类似于 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;
}
看看xkcd-style graphs,一种向图形添加随机噪声并以使其看起来像手绘的方式平滑它的方法,模仿xkcd 卡通的风格。虽然这不是您想要的,但我认为只需将输入图设置为零(例如 y=0)并将噪声平滑参数调整为更大的噪声幅度和更大的平滑距离可能会导致随机图的类型你正在寻找。
public static double GetCosFunction(double d)
{
return Math.Cos(d) * 50 + 50;
}
对于图形解决方案,您可以查看这些主题;