给定一个开始、结束和增量值,我想要一个向上和向下计数的算法
Posted
技术标签:
【中文标题】给定一个开始、结束和增量值,我想要一个向上和向下计数的算法【英文标题】:Given a start, end, and increment value, I want an algorithm that counts up and down 【发布时间】:2009-09-15 23:54:47 【问题描述】:好的,我有一个想要淡入淡出(连续)的控件。为了淡化它,我在绘图例程中调整控件的透明度值。所以我设置了一个运行的计时器并调整了 m_transparency 成员。计时器运行,它应该在两个定义的值 m_start、m_end 之间来回扫描。这些可以是 0-255。
例如。 开始 30,结束 55,增量值 = 5。它看起来像:
30、35、40、45、55、50、45、40、35、30、35、40 ......
还应该处理开始 55,结束 30。
无论如何,我写了一个可怕的函数,它基本上测试了每个条件(我是向上还是向下移动,我是否到达了起点、终点、转身点等)。这很丑陋,我很尴尬(三重嵌套 if),但我想不出一种更清洁的方法来测试所有内容。有没有更简单的方法?
【问题讨论】:
@liori: 86 字符: 'f(intv,int x,int y,ints)*s=(*v==x||*v ==y)?-*s:*s;*v=(*v+*s下面的课程相当简单。您需要考虑您希望它在边界情况下的行为方式(例如,这将为 RangeScanner(30, 54, 5) 发出 55,但很容易将其更改为您想要的行为)。
类 RangeScanner 上市: RangeScanner(int start, int end, unsigned int inc): 值(开始), 较低(标准::分钟(开始,结束)), 上(标准::最大值(开始,结束)), 增量(INC), 递增(开始 = 上 || 值
【讨论】:
“递增”变量可以编码为“递增”变量的符号。 真的吗?负增量表示减量是一个很常见的习语。对于像这样简单的事情,我个人认为无关的变量会分散注意力。 YMMV。 作为一般规则,我更喜欢将不同的对象状态分开。它让一些白痴更难出现并在以后破坏它,并减少所需的错误检查量 - 考虑有人误解了参数并试图创建 RangeScanner(55, 30, -5)。【参考方案2】:这就是我的处理方式,一个基于 C 的解决方案(当然可以在 C++ 中工作,但没有任何 C++ 功能):
int nextVal (int curr, int min, int max, int *pStep)
// Handle situations where you want to turn around.
if ((curr == min) || (curr == max))
*pStep = -(*pStep);
return curr + *pStep;
// Handle situation where you would exceed your bounds (just
// go to the bound).
if (curr + *pStep < min) return min;
if (curr + *pStep > max) return max;
// Otherwise, just move within the bounds.
return curr + *pStep;
int main(void)
int i;
// Set up your variables here.
int min = 30;
int max = 55;
int step = 5;
// This is the running current value and we need to negate the step
// to force initial turn-around.
int curr = min;
step = -step;
// Simple loop to demonstrate.
printf ("%d\n", curr);
for (i = 19; i > 0; i--)
curr = nextVal (curr, min,max,&step);
printf ("%d\n", curr);
return 0;
这个输出:
30, 35, 40, 45, 50, 55, 50, 45, 40, 35, 30, 35, 40, 45, 50, 55, 50, 45, 40, 35
为您的测试用例。它还可以智能地处理范围不是增量的精确倍数的序列(例如,[7,40,10]):
7, 17, 27, 37, 40, 30, 20, 10, 7, 17, 27, 37, 40, 30, 20, 10, 7, 17, 27, 37
【讨论】:
if ((curr = max)) 没必要,@ralu,该值永远不允许超出界限,如果出于某种奇怪的原因,调用者在这些界限之外开始使用它,第一步将把它带回来。 【参考方案3】:以下是作为time
函数的线性上升-下降的基本方法。
int triangle(int time)
int d = m_end - m_start;
return m_start + abs(((m_increment * time + d) % 2*d) - d);
【讨论】:
【参考方案4】:如何使用正弦函数来获得更自然的渐变效果...
#include <math.h>
class SineFader
public:
SineFader(int min, int max)
: base((double)min + ((double)(max - min) / 2))
, range((double)(max - min) / 2)
, theta(4.71)
, speed(0.1)
int getValue()
theta += speed;
return (int)(base + (range * sin(theta)));
private:
double base, theta, range, speed;
;
这是您在代码中使用它的方式:
SineFader myfade(0, 55);
void onTimer()
setTransparency(myfade.getValue());
【讨论】:
好主意,但你应该检查你的数学。当 theta = 4.7 时会发生什么? 这就是我从袖口敲出代码的结果。感谢您发现那个 bk1e。我已经修复了一点,但它使构造函数更加复杂。 有没有以毫秒为单位设置脉冲周期而不是速度? 这取决于您调用 onTimer() 的频率以及您设置的速度值。公式是每闪一次 (timer_interval * ((2 * PI) / speed))。【参考方案5】:我的版本重复了限制值:... 50, 55, 55, 50, ..., 35, 30, 30, 35, ...
struct UpDown
int lo_val, hi_val, curr_val;
int step;
;
void fade_in_out(struct UpDown *x)
x->curr_val += x->step;
if ((x->curr_val > x->hi_val) || (x->curr_val < x->lo_val))
x->step *= -1;
x->curr_val += x->step;
int main(void)
struct UpDown myControl = 30, 55, 30, 5;
for (;;)
fade_in_out(&myControl);
return 0;
【讨论】:
【参考方案6】:这个很灵活,您可以从范围内的任何位置开始,也可以通过将 dir 设置为 1 或 -1 来指定要开始的方向。
class Fader
public:
Fader(int min, int max, int start, int dir = 1)
: _min(min)
, _max(max)
, _val(start)
, _dir(dir)
int getValue()
if(_val <= min || _val >= _max)
_dir = -_dir;
_val += _dir;
return _val;
private:
int _min, _max, _dir, _val;
;
【讨论】:
这实际上是相当脆弱的。试试推子 (0, 10, 20, 2)。另外,我希望 Fader(0, 10, 0, 1).getValue() 返回 0,但它返回 1(即“start”不是返回的第一个值)。 确实如此,但如果初始值合理,它就可以正常工作。您可以在构造函数中添加对错误初始值的检查。【参考方案7】:我就是这样做的。看起来很简单。
int computeTransparency(int start, int end, int& increment, int oldVal)
if (start > end)
swap(start, end);
int newVal = oldVal + increment;
bool goingUp = increment > 0;
if (goingUp)
if (newVal >= end)
newVal = end;
increment = -increment;
else
if (newVal <= start)
newVal = start;
increment = -increment;
return newVal;
【讨论】:
【参考方案8】:这是另一种方法,使用辅助函数在 start 和 start+span 之间以增量的增量弹跳:
// Goes from start to start + span, and repeats.
int ramp(start, increment, span)
static int lastval = start;
lastval += increment;
lastval = lastval % (span + 1);
return lastval;
// Goes from start up to start+span and back down, and repeats.
int bounce(start, increment, span)
val = ramp(0, increment, 2*span - 1);
if (val > span) val = span - val;
return val + start;
本质上,这是在创建一条不断增加的线,然后使用 MOD 函数将其切割成锯齿,然后将锯齿的上半部分向下反射以形成锯齿形。
请注意,与所介绍的所有其他实现不同(除非我错过了一个),即使增量不均分 - 例如,对于 0 ,跨度为 10,步长为 3,它将上升 3 到 3,上升 3 到 6,上升 3 到 9,上升 1,下降另一个 2 到 8,下降 3 到 5,下降 3 到 2,向下 2 向上 1 到 1,依此类推 - 3, 6, 9, 8, 5, 2, 1, 4, 7, 10, 7, 4, 1, .... 它还保证即使增量远大于跨度或为负数,数字始终在正确的范围内并做正确的事情。
(您可能应该重写它以将诸如 start、increment 和 span 之类的东西放在静态存储中而不是函数参数中,因为如果它们发生变化,这会变得相当混乱,但我对此感到很懒惰。而且,真的,它仍然会落在正确的范围内,并且如果它们发生变化,它会做一些明智的事情——你可以在任何时候改变增量,它会加速或减速而不跳动。)
【讨论】:
以上是关于给定一个开始、结束和增量值,我想要一个向上和向下计数的算法的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在网页滚动视图中有一个指示器 - 我的意思是我想要一个可以按住并向上/向下滚动的支架/按钮?
2021-06-04:给定三个参数:二叉树的头节点head,树上某个节点target,正数K,从target开始,可以向上走或者向下走。返回与target的距离是K的所有节点。