背景
我已经成功编写了生成从0到2pi的正弦波的代码。调整常量xPrecision
和yPrecision
,可以水平或垂直拉伸图形。
当xPrecision = yPrecision = 10
:
我的查询:
我现在希望显示数字0到9而不是星号。因此,最左边的星被0替换,第二个最左边的星被1替换,依此类推。当你达到9时,下一个数字再次为零。
我对如何做到这一点毫无头绪。我已经看过波形模式like this,但它们是固定的宽度模式,而我的是可扩展的。
我能想到的唯一方法是将输出转换为2D字符数组,然后从左到右手动抓取*
,并用数字替换它们,然后打印它。但是,在较大的x/yPrecision
值时,这会耗费大量内存。
实现此输出的最佳方法是什么?
打印正弦波的代码:
class sine {
static final double xPrecision = 10.0; // (1/xPrecision) is the precision on x-values
static final double yPrecision = 10.0; // (1/yPrecision) is the precision on y-values
static final int PI = (int) (3.1415 * xPrecision);
static final int TPI = 2 * PI; // twice PI
static final int HPI = PI / 2; // half PI
public static void main(String[] args) {
double xd;
for(int start = (int) (1 * yPrecision), y = start; y >= -start; y--){
double x0 = Math.asin(y / yPrecision),
x1 = bringXValueWithinPrecision(x0),
x2 = bringXValueWithinPrecision(x0 + TPI / xPrecision),
x3 = bringXValueWithinPrecision(PI/xPrecision - x0);
// for debug
//System.out.println(y + " " + x0 + " " + x1 + " " + x2 + " " + x3);
for(int x = 0; x <= TPI; x++){
xd = (x / xPrecision);
if(x1 == xd || x2 == xd || x3 == xd)
System.out.print("*");
else System.out.print(" ");
}
System.out.println();
}
}
public static double bringXValueWithinPrecision(double num){
// obviously num has 16 floating points
// we need to get num within our precision
return Math.round(num * xPrecision) / xPrecision;
}
}
答案 0 :(得分:8)
首先在内存中“绘制”图形,然后将数字分配给其垂直点,并在单独的传递中打印它们。
01
9 2
8 3
7 4
6 5
5 6
4 7
3 8
2 9
1 0
0 1 2
2 1
3 0
4 9
5 8
6 7
7 6
8 5
9 4
0 3
12
请参阅代码中的注释,了解其工作原理:
static final double xPrecision = 10.0; // (1/xPrecision) is the precision on x-values
static final double yPrecision = 10.0; // (1/yPrecision) is the precision on y-values
static final int PI = (int) (3.1415 * xPrecision);
static final int TPI = 2 * PI; // twice PI
static final int HPI = PI / 2; // half PI
public static void main(String[] args) {
// This part is the same as OP's code, except that instead of printing '*'
// it stores the corresponding row number in the array of rows
double xd;
int[] row = new int[100];
Arrays.fill(row, -1);
int r = 0;
int maxc = 0; // Mark the rightmost column of all iterations
for(int start = (int) (1 * yPrecision), y = start; y >= -start; y--){
double x0 = Math.asin(y / yPrecision),
x1 = bringXValueWithinPrecision(x0),
x2 = bringXValueWithinPrecision(x0 + TPI / xPrecision),
x3 = bringXValueWithinPrecision(PI/xPrecision - x0);
int c = 0;
for(int x = 0; x <= TPI; x++, c++){
xd = (x / xPrecision);
// This is where the asterisk used to go
if(x1 == xd || x2 == xd || x3 == xd)
row[c] = r;
}
maxc = Math.max(c, maxc);
r++;
}
// Walk the assigned rows, and give each one a consecutive digit
int[] digit = new int[100];
int current = 0;
for (int i = 0 ; i != 100 ; i++) {
if (row[i] != -1) {
digit[i] = (current++) % 10;
}
}
// Now walk the rows again, this time printing the pre-assigned digits
for (int i = 0 ; i != r ; i++) {
for (int c = 0 ; c != maxc ; c++) {
if (row[c] == i) {
System.out.print(digit[c]);
} else {
System.out.print(' ');
}
}
System.out.println();
}
}
public static double bringXValueWithinPrecision(double num){
// obviously num has 16 floating points
// we need to get num within our precision
return Math.round(num * xPrecision) / xPrecision;
}
代码的第一部分填充row[i]
数组,其中包含i
列中星号的行。来自row[]
数组的前几个数字看起来像这样:
10 9 8 7 6 5 4 - 3 2 - 1 - - - 0 0 - - - 1 - 2 3 - 4 5 6 7 8 9 10
-
表示-1
的单元格,表示缺失值。该数组表示最左边的星号位于第10行,下一个星号位于第9行,然后是8,7,6,依此类推。星号11和12在行0上,位于顶部。
第二个循环遍历rows
,跳过-1
,并为所有非负位置分配连续数字。
当当前行与digit[]
数组中的值匹配时,第三个循环将逐行遍历整个字段,从预先分配的row[]
数组打印值。
答案 1 :(得分:4)
如果替换:
System.out.print("*");
与
System.out.print(""+(x%10));
似乎几乎工作。
56
1 0
9 2
8 3
6 5
5 6
4 7
3 8
2 9
1 0
0 1 2
2 1
3 0
4 9
5 8
6 7
7 6
9 4
0 3
2 1
67
也许进一步的调整可能会让它变得完美。
答案 2 :(得分:2)
以完全不同的方式进行操作会产生不同的画面,但会达到你的效果。
基本上,
for each y
for each x
calculate fx = sin(x)
if fx == y print * else print space
效率非常低,因为它会计算sin(x)
x*y
次,如果您填充矩阵,则可以sin(x)
只计算x
次。
static final double xPrecision = 10.0; // (1/xPrecision) is the precision on x-values
static final double yPrecision = 10.0; // (1/yPrecision) is the precision on y-values
private void sine() {
for (double y = 1; y >= -1; y -= 1.0 / yPrecision) {
int n = 0;
for (double x = 0; x < 2.0 * Math.PI; x += 1.0 / xPrecision, n++) {
double fx = Math.sin(x);
boolean star = Math.round(fx*xPrecision) == Math.round(y*yPrecision);
System.out.print((star ? ""+(n%10) : " "));
}
System.out.println();
}
}
public void test(String[] args) {
sine();
}
给你:
345678
12 901
90 2
8 34
67 5
5 6
4 7
3 8
2 9
1 0
0 1
2 2
3 1
4 0
56 9
7 8
8 67
9 5
01 34
23 12
4567890
答案 3 :(得分:1)
这是我的解决方案,它基本上使用了4个for循环的正弦的一半:
在每个循环中只替换第一个星号。
class sine {
static final double xPrecision = 14.0; // (1/xPrecision) is the precision on x-values
static final double yPrecision = 14.0; // (1/yPrecision) is the precision on y-values
static final int PI = (int)(3.1415 * xPrecision);
static final int TPI = 2 * PI; // twice PI
static final int HPI = PI / 2; // half PI
public static void main(String[] args) {
double xd;
String str = "";
for (int start = (int)(1 * yPrecision), y = start; y >= -start; y--) {
double x0 = Math.asin(y / yPrecision),
x1 = bringXValueWithinPrecision(x0),
x2 = bringXValueWithinPrecision(x0 + TPI / xPrecision),
x3 = bringXValueWithinPrecision(PI / xPrecision - x0);
// for debug
//System.out.println(y + " " + x0 + " " + x1 + " " + x2 + " " + x3);
for (int x = 0; x <= TPI; x++) {
xd = (x / xPrecision);
if (x1 == xd || x2 == xd || x3 == xd)
str += "*";
else str += " ";
}
str += "\n";
}
String[] rows = str.split("\n");
int half = (int)(1 * yPrecision);
// we use this half in for loops, from half to 0, from 0 to half, from half to the end and from the end to the half, and replace only the first asterisk.
int val = 0;
for (int i = half; i >= 0; i--) {
if (val == 10) val = 0;
rows[i] = rows[i].replaceFirst("\\*", Integer.toString(val++));
}
for (int i = 0; i <= half; i++) {
if (val == 10) val = 0;
rows[i] = rows[i].replaceFirst("\\*", Integer.toString(val++));
}
for (int i = half + 1; i < rows.length; i++) {
if (val == 10) val = 0;
rows[i] = rows[i].replaceFirst("\\*", Integer.toString(val++));
}
for (int i = rows.length - 1; i >= half; i--) {
if (val == 10) val = 0;
rows[i] = rows[i].replaceFirst("\\*", Integer.toString(val++));
}
System.out.println(String.join("\n", rows));
}
public static double bringXValueWithinPrecision(double num) {
// obviously num has 16 floating points
// we need to get num within our precision
return Math.round(num * xPrecision) / xPrecision;
}
}
结果:
01
9 2
8 3
7 4
6 5
5 6
4 7
3 8
2 9
1 0
0 1 2
2 1
3 0
4 9
5 8
6 7
7 6
8 5
9 4
0 3
12
答案 4 :(得分:1)
通过使用每行有一个点(在每个斜率上)的事实,您可以计算在每个点显示哪个数字而不使用额外的内存或循环。这是我的例子。请注意,我只检查过此示例仅在xPrecision
和yPrecision
为整数时才有效。如果你想使用双打,你必须修改它。
class sine {
static final double xPrecision = 10.0; // (1/xPrecision) is the precision on x-values
static final double yPrecision = 10.0; // (1/yPrecision) is the precision on y-values
static final int PI = (int) Math.round(Math.PI * xPrecision);
static final int TPI = 2 * PI; // twice PI
static final int HPI = PI / 2; // half PI
static final int cycles = 2; // prints from x=0 to 2*cycles*pi
public static void main(String[] args) {
double xd;
int cycleoffset, cycleoffset2, topbottomoffset = 1;
for (int start = (int) (1 * yPrecision), y = start; y >= -start; y--) {
double x0 = Math.asin(y / yPrecision), x1 = bringXValueWithinPrecision(x0),
x2 = bringXValueWithinPrecision(x0 + TPI / xPrecision),
x3 = bringXValueWithinPrecision(PI / xPrecision - x0), tmp;
if (y == start) {
if (x1 == x3) // when there is only one point at the top/bottom
topbottomoffset = 0;
else if (x1 > x3) // swap x1 and x3
{
tmp = x1;
x1 = x3;
x3 = tmp;
}
} else if (y == -start) {
// I don't think this is needed, but just for safety make sure there is only one point on the bottom if there was only one point at the top
if (topbottomoffset == 0)
x2 = x3;
else if (x2 < x3) // swap x2 and x3
{
tmp = x2;
x2 = x3;
x3 = tmp;
}
}
cycleoffset = (int) (4 * yPrecision + 2 * topbottomoffset);
cycleoffset2 = -cycleoffset;
int start1 = topbottomoffset + 2 * (int) yPrecision, start2 = 2 * topbottomoffset + 4 * (int) yPrecision;
for (int x = 0, lim = cycles * TPI; x <= lim; x++) {
xd = ((x % TPI) / xPrecision);
if (x % TPI == 0)
cycleoffset2 += cycleoffset;
// x = 0 to pi/2
if (x1 == xd)
System.out.print((cycleoffset2 + y) % 10);
// x = 3pi/2 to 2pi
else if (x2 == xd)
System.out.print((cycleoffset2 + start2 + y) % 10);
// x = pi/2 to 3pi/2
else if (x3 == xd)
System.out.print((cycleoffset2 + start1 - y) % 10);
else
System.out.print(" ");
}
System.out.println();
}
}
public static double bringXValueWithinPrecision(double num) {
// obviously num has 16 floating points
// we need to get num within our precision
return Math.round(num * xPrecision) / xPrecision;
}
}
修改强> 不同范围的数字计算如下
0&lt; x&lt; π/ 2:这个是最简单的,因为它是第一个范围。由于中间行是y = 0,这是正弦波开始的地方,我们可以使用y
来找到数字。
π/ 2&lt; x&lt; 3π/ 2:这里的数字随着我们的下降而计算,但y
随着我们的下降而减少。所以我们必须使用-y
术语。在第一行y=yPrecision
,前一个范围的最后一位是yPrecision
。所以我们使用2*yPrecision - y
,因为它包含-y
,并且等于第一项的yPrecision(其中y = yPrecision)。
3π/ 2 < x&lt; 2π:这里的数字随着我们的下降而倒计时,所以我们需要一个+y
项,但棘手的部分是确定从哪里开始。由于此时的正弦波从0变为y精度到0到-y精度,因此底点(x =3π/ 2)应从3 * y精度开始。由于底部的y = -yPrecision,我们使用4*yPrecision + y
,因为它包含+y
并且在第一项(等于y = -yPrecision)时等于3 * yPrecision。
topbottomoffset
术语:根据用于xPrecision和yPrecision的值,可以在顶行和底行绘制一个或两个点。如果有两个点,我们需要在π/ 2到3π/ 2范围内加一个数字,在3π/ 2到2π范围内加两个数字。
cycleoffset
项:如果绘制了多个正弦波周期,则需要从上一个周期中使用的最后一个数字开始附加周期。每个周期从0到y精度到0到-yPrecision为0,等于4 * yPrecision。因此,每个新周期都需要从4 * y精度* [先前周期数]开始。如果顶行和底行有两个点,那么也需要将它们考虑在内。
交换值:当顶行有两个点时,x1>x3
。发生这种情况是因为在y=yPrecision
时,我们正在使用Math.asin(1)
,这恰好是Java双系统中的pi/2=1.5707963267948966
。在xPrecision
<100.0
下方,由bringXValueWithinPrecision
完成的舍入会使x1
最多1.58
,而x3
最多可达1.56
res = {"USD_GBP":{"val":0.73897}};
res[Object.keys(res)[0]].val
}。因此,需要交换它们以获得正确的数字顺序。
答案 5 :(得分:1)
由于这是Java,让我们如何实际使用某些对象作为对象,而不仅仅是定义一些函数的地方。
将您的波浪图视为几个不同&#34;分支的组合&#34;反正弦函数。 (数学上,这就是我们如何解释你的程序版本使用Math.asin
为星星生成多个坐标的方式。)
分支0是曲线的初始上升部分,
分支1是分支0后曲线的下降部分,
分支2是分支1之后的曲线的上升部分,依此类推。
分支在x值0处越过输出的中间线,
PI,2 * PI,3 * PI,等等。
根据您希望图表向右延伸的距离,可以轻松确定所需的分支数量。
例如,要绘制从0到8 * PI,您需要九个分支
(分支0,分支8,以及这两者之间的七个分支)。
您可以使用某个类的对象实现每个分支,
我们称之为ArcSineBranch
。
它有一个构造函数ArcSineBranch(int)
,它将分支编号作为参数。
创建某种有序列表(可能只是一个ArcSineBranch[]
数组)并将这些分支对象放入其中,
确保分支编号顺序从0到所需的最大编号。
您还希望以某种方式告诉第一个ArcSineBranch
最左端的位置 - 在问题的示例中,第一个分支的最左端是y == 0
,而对于所有其他上升分支,它位于y == -start
,而对于所有下降分支,它位于y == start
。
现在你调用第一个ArcSineBranch
的mutator函数,它告诉它最左边的符号是0
。现在将其视为整数(而不是字符串),以使算术更容易。
然后,您查询第一个ArcSineBranch
以查找它将写入的最右边的符号,它可以从最左边的符号和它将在其上写入符号的行数进行计算。
您还可以查询最右侧符号的x坐标。
(该对象通过从Math.asin(y / yPrecision)
的倍数中加上或减去PI
的舍入倍数来计算任何y坐标的符号的x坐标。)
现在对于列表中的每个ArcSineBranch
,将最右边的符号和前一个分支写的x坐标传递给它。
此ArcSineBranch
使用该信息来确定它所写的最左边的符号和该符号的y坐标。
(我在这里注意y坐标,如果你选择xPrecision
的值,导致一个分支的最右边的x坐标与下一个分支的最左边的x坐标相同;我们应该只写一个符号在输出中的那个地方,所以我们希望后面的分支跳过它最左边的x坐标并在下一个地方写下它最左边的符号,一行向上或向下。但是如果x坐标不同,我们希望后面的分支写一个符号在同一行。)
现在后来ArcSineBranch
&#34;知道&#34;它将打印的最左边的符号和符号的y坐标,您可以查询它的最右边的符号和x坐标,并将它们传递给下一个ArcSineBranch
,依此类推。
以这种方式遍历所有ArcSineBranch
个对象后,
这样每个对象都知道需要为其分支写入哪些符号以及在何处编写它们,您可以循环for (y = start; y >= -start; y--)
;
在该循环中,您遍历ArcSineBranch
个对象的列表;
对于每个对象,您查询是否需要写入符号
y坐标y
。
如果对象需要写入符号,
你查询哪个符号写在哪个x坐标,
然后将输出间隔到右边,直到到达那个x坐标并在那里写出那个符号。
但是,当然,首先检查一下,这不会划出一个符号
所需图形的右边缘。
(此检查实际上仅适用于最后一个ArcSineBranch
,因此您可以先通过循环其他分支然后分别处理最后一个ArcSineBranch
来优化代码。)
我已经比我最初想要的更详细地描述了这个算法。这里应该有足够的信息以相对简单的方式将其编码到Java中,尽管仍有一些本地化的细节需要解决。
请注意,本答案中的设计旨在使用与问题中的代码相同的数学思想来决定在何处绘制点。
具体而言,ArcSineBranch(0)
生成原始代码中的x1
值,ArcSineBranch(1)
生成x3
值,ArcSineBranch(2)
生成x2
值。
此设计的实现应在原始代码绘制的每颗星的位置绘制一个数字,并且不应绘制其他数字。
答案 6 :(得分:1)
关心不同的方法?
3030
28 28
26 26
22 22
18 18
12 12
06 06
00 00 00
06 06
12 12
18 18
22 22
26 26
28 28
3030
解决方案:
import static java.lang.Math.sin;
import static java.lang.Math.PI;
import static java.lang.Math.abs;
public class Sine {
static final Integer points = 30; // points on x and y axis
public static void main(String[] args) {
// contains graph points
Boolean[][] graph = new Boolean[points + 1][points + 1];
for (Double x = 0.0; x <= points; x++) {
// x axis pi value
Double pi = (x / points) * 2 * PI;
// sin(x) plot for x
Integer sinx = (int) Math.round((sin(pi) * points / 2) + points / 2);
graph[sinx][x.intValue()] = true;
}
for (Integer i = 0; i <= points; i++) {
for (Integer j = 0; j <= points; j++) {
// space characters on x axis
Integer pt = (int) Math.floor(Math.log10(points) + 1);
String space = String.format("%" + pt + "s", " ");
// padding for p
String p = String.format("%0" + (pt) + "d", abs(i - points / 2) * 2);
System.out.print(graph[i][j] != null ? p : space);
}
System.out.println();
}
}
}
方法:
points
包含x和y轴上的字符数。
graph
包含每个x和y字符的true
或null
。
第一次循环:
由于正弦图中x的值来自0 to 2π
,我们需要相应地转换x。因此,pi
包含相同范围的值,但符合x
。
sinx
是根据Integer
的{{1}}值。
无需解释x
。
第二个循环:
第一次循环
执行LOOPLABEL。
最后分到下一行。
第二次循环(LOOPLABEL)
graph[sinx][x.intValue()] = true;
保存y轴上填充的数字。
pt
是要在y轴上打印的空格字符。
space
是0到p
之间的转换范围。
打印points
<强> DEMO 强>
答案 7 :(得分:-2)
在循环中添加一个计数器,并在达到9时重置它:
for(int x = 0, counter = 0; x <= TPI; x++, counter++){
xd = (x / xPrecision);
if(x1 == xd || x2 == xd || x3 == xd) {
System.out.print("" + counter);
if (counter == 9) {
counter = 0;
}
} else {
System.out.print(" ");
}
}