我可以这样描述这个数列,这个数列起始值为100,f(0)=100,长度为200,这个数列是递增的,递增的速度先快后慢。当到100时每次累加的值为最大值,随后累加值逐步递减,直到200时,也就是数列的最后一位时,累加值为0。求该数列计算公式,其中系数k控制数列跳跃幅度,从而来影响数列最后一位值的大小。
———————————
截止当前已回答的答案我都进行了认真的分析和思考,在此感谢各位的关注。
其实我想要的是一个公式,这个公式是给出数列的下标n计算出该位置的值,即 f(n)=x。
举个栗子,该数列最简单的形式为累加量为1,即数列临近两个累加值相差为1, 如f(0)为起始即
f(0)=100;
f(1)=101,fˊ(1)=1;
f(2)=103,fˊ(2)=2;
f(3)=106,fˊ(3)=3;
f(4)=110,f'(4)=4;
f(5)=115,f'(5)=5;
从以上可以看出该数列前6位的值分别为100,101,103,106,110,115。
累加值分别为1,2,3,4,5。累加量为1。
公式为:
f(n)=f(n-1)+f'(n),因为f'(n)=n,所以公式可以简化为f(n)=f(n-1)+n;
在n <99的情况下
f(n)=100+(n+1)*n/2,f'(n)=n;
f(99)=100+100*99/2=5050,f'(99)=99,
此时累加值99为最高点;
f(100)=5050+98=5148;
f(101)=5148+97=5245;
f(102)=5245+96=5341;
f(199)=?
———————————
以上示例为累加量为1的情况,我现在需要的是一个公式,在该公式下,累加量(系数)可以是任何自然数,通过数列下标求出该位置的数列的值即f(n)=x;
需要强调两点,第一该数列是递增数列,这个条件一定要满足,第二就是累加量一定是连续的,呈抛物线态。
以下为对回答的解析:
@边城 算法解析:
为了验证稍微对代码进行了调整:
function createIncrementGenerator(k) {
return function (x) {
const t = x / 100 - 1;
return k - k * t * t;
};
}
const k = 1000; // 幅度参数
const getIncrement = createIncrementGenerator(k);
let n = 100;
var tmp = 100;
for (let i = 0; i < 200; i++, n += Math.round(getIncrement(i))) {
console.log("f("+i+")="+n+",f'("+i+")="+(n-tmp));
tmp=n;
}
当K等于100时,增量最大值为100,在最高点出现了15次;
当K等于200时,增量最大值为200,在最高点出现了11次;
当K等于500时,增量最大值为500,在最高点出现了7次;
当K等于800时,增量最大值为800,在最高点出现了5次;
当K等于1000时,增量最大值为1000,在最高点出现了5次;
当K等于2000时,增量最大值为2000,在最高点出现了3次;
当K等于5000时,增量最大值为5000,在最高点出现了3次;
从结果可以看出,K值其实就是增量的最大值,这个设定挺好便于理解,经过验证当K小于1300时,在最高点出现相同增量次数大于3,这样就趋于直线,即会出现超过3个数的增量是相同的。
0326更新
关于@边城的算法,再进行分析,通过交流和分析得知:出现直线是因为取整造成。在这种情况下,如要使用整数数列,就需要增大K的值,这样就可以解决这个问题。
通过观察f'(0)~f'(199)数列的规律,确定是开口向下的抛物线,这个没有问题。但是如果将f'(0)~f'(199)数列的增量再进行组合即f''(0)~f''(199)其实并不是均匀分布,呈一定的规律递减,未再深入分析,图形为斜线。
function createIncrementGenerator(k) {
return function (x) {
const t = x / 100 - 1;
return k - k * t * t;
};
}
const k = 8000; // 幅度参数
const getIncrement = createIncrementGenerator(k);
let n = 100;
let tmp1 = 100, tmp2 = 100,tmp3=100;
for (let i = 0; i < 200; i++, n += Math.round(getIncrement(i))) {
tmp2 = n-tmp1;
document.write("f("+i+")="+n+", f'("+i+")="+tmp2+" ,f''("+i+")="+(tmp2-tmp3)+"<br>");
tmp1=n;
tmp3=tmp2;
}
接下来分析@hfhan的算法,根据作者要求,将原代码进行了改造增加了K因子,同时为了方便观察对输出也进行了改动,代码如下:
let num = 100, count = 199, x = 0;
let tmp1 = 0, tmp2 ,tmp3=0;
let k = 2; //// 幅度参数
while (x <= count) {
num += k * (-x*x + count*x);
x++;
tmp2 = num-tmp1;
document.write("f("+x+")="+num+", f'("+x+")="+tmp2+" ,f''("+x+")="+(tmp2-tmp3)+"<br>");
tmp1=num;
tmp3=tmp2;
}
该算法更简洁一点。通过观察数列的增幅较大,K的控制幅度直接影响的是f''的增幅,即f''(n)=2*K,且f''(0)~f''(199) 是均匀分布,增量递减,增幅为定值,图形为斜线。
0327 更新
本次分析@未觉雨声的算法,为了不破坏原方法,我在外部进行调用(不考虑性能),另外对原方法中对返回值取整,代码如下:
function f(pos, size = 199, start = 100, ratio = 100) {
const center = (size + 1) / 2;
const speed = i => ratio - ratio * ((i / center - 1) ** 2);
let prev = start
for (let i = 0; i <= pos; ++i) {
prev += speed(i)
}
return Math.round(prev);
}
let size = 200, start = 100, K = 8000;
let num;
let tmp1 = 100, tmp2 = 100, tmp3 = 0;
for (let i = 0; i <= size; ++i) {
num = f(i, size, start, K);
tmp2 = num-tmp1;
document.write("f("+i+")="+num+", f'("+i+")="+tmp2+" ,f''("+i+")="+(tmp2-tmp3)+"<br>");
tmp1=num;
tmp3=tmp2;
}
通过对结果的观察,也满足本题目标,递增数列,f'呈抛物线,算法基本上与@边城算法相同,因子K也是增量的最大值,即抛物线的最高点,算法有轻微差异,见下截图
接下来@xdsnet的公式,虽然没有给出具体的算法,但是通过回复的内容进行分析,其思路与@边城的算法基本相同,在这里不做过多赘述,再次感谢@xdsnet。
总结:
至此,感谢各位对本问题的关注,感谢@边城对算法的详尽分析,感谢@xdsnet的互动讨论,同时也感谢@未觉雨声和@hfhan提供的算法。
其实一些问题在开始的时候并没有考虑清楚,可以说是在讨论和思考的过程中发现的,比如在实际场景中需要使用的是整数数列,这个在前面确实没有写清楚,还有就是对f''(0)~f''(199)的分布状态也没有考虑到,这个只是在各位算法出来之后才发现,经过对实际场景需求进行分析,f''的均匀分布更适合需求,只是@hfhan的数列增幅幅度较大,但是对目前来说已经超出了我的预期。
0329 更新
补充分析@边城 整数递归算法
该算法虽然把一些条件在算法中写死,但是对数列的控制更精准,通过对打印的结果分析,f'为抛物线,且均匀分布,增幅为k,即f''(n)=|k| 为常量。令我惊奇的是这个算法居然实现了正文示例的数列。
function f(n, m = 1) {
if (n <= 0) { return 100; }
if (n > 198) { return 0; }
if (n < 100) {
return f(n - 1, m) + n * m;
} else {
return f(n - 1, m) + (198 - n) * m;
}
}
let K = 10;
let num;
let tmp1 = 100, tmp2 = 100, tmp3 = 0;
for (let i = 0; i <= 200; ++i) {
num = f(i, K);
tmp2 = num-tmp1;
document.write("f("+i+")="+num+", f'("+i+")="+tmp2+" ,f''("+i+")="+(tmp2-tmp3)+"<br>");
tmp1=num;
tmp3=tmp2;
}
再次感谢@边城提供的算法!
至此,本问题可以关闭了,原打算使用@hfhan的算法,但是今天却发现@边城的整数数列的算法更贴近我的需要。
把最真的祝福化作风,吹送到每一位帮助我的人的身边,把最诚的问候变成雨,飘散到每一位帮助我的人的窗前,把我的感谢化作万语千言,为你送来的最真诚的谢意,平安幸福。
在文中如果有描述错误,或者理解有误请告知,感谢~
回答二(整数递归)
难道说你想要的是一个递归函数?
按你的说法,
f(99)
就是顶点,之后增就变成了 98,也就是说f(100) = f(99) + 98
,f(101) = f(100) + 97
…… 即f(n) = f(n - 1) + (198 - n)
,这样算下来 n 到不了 200,到 198 的时候,增量就已经是 0 了。回答一(二次函数抛物线)
先说增值部分, 这部分是从 0 开始,到 100 最高,到 200 回到 0 —— 可以想像这个曲线,而且很容易判断出来是这个二次函数,其顶点在 (100, k),而且经过了 (0, 0) 和 (200, 0) 这两个点。
如果抛物线的顶点在 (h, k),那么这个抛物线的顶点式为:
$$ y = a(x - h)^2 + k $$
代入 (100, h),得
$$ y = a(x - 100)^2 + k $$
因为,它过 (0, 0) 点,代入 x,y 得
$$ \begin{align} &0 = a(0 - 100)^2 + k\\ \Rightarrow&a=-\frac{k}{10000} \end{align} $$
在顶点式中代入 a 可得方程(用 f(x) 表示 y)
$$ \begin{align} & f(x) = -\frac{k}{10000}*(x - 100)^2+k\\ \Rightarrow & f(x)=-k(\frac{x}{100} - 1)^2+k \end{align} $$
这里 k 就是一个可调节参数,可以用于控制幅度(如动图)
剩下的就是一个累加循环了,程序如下(JavaScript 描述,数列元素不保证整数)
再来段 C# 的代码(取整了的)