学习 › TypeScript
进阶 · 23
绝对值、乘方、平方根
去掉符号、连乘多次、求平方后等于某数的值。
这是四则运算之后最常用的三种计算。
下面的例子读入两个点的坐标,输出它们在横向和纵向上相隔几格,再输出两点之间的距离(保留两位小数),最后输出不超过这个距离的最大整数。
import math x1, y1, x2, y2 = map(int, input().split()) dx = abs(x2 - x1) # 3dy = abs(y2 - y1) # 1 - 7 = -6 → 6print(dx, dy) d2 = dx ** 2 + dy ** 2 # 45, 정수끼리라 정수 그대로dist = math.sqrt(d2) # 6.708203932499369print(f"{dist:.2f}") # 정수 제곱근: r * r <= d2인 가장 큰 정수r = int(dist)while r * r > d2: r -= 1while (r + 1) * (r + 1) <= d2: r += 1print(r) # math.isqrt(d2)도 같은 답const [x1, y1, x2, y2] = require("fs").readFileSync(0, "utf8").trim().split(" ").map(Number); const dx = Math.abs(x2 - x1); // 3const dy = Math.abs(y2 - y1); // 1 - 7 = -6 → 6console.log(dx, dy); const d2 = dx ** 2 + dy ** 2; // 45const dist = Math.sqrt(d2); // 6.708203932499369console.log(dist.toFixed(2)); // 정수 제곱근: r * r <= d2인 가장 큰 정수let r = Math.floor(dist);while (r * r > d2) r--;while ((r + 1) * (r + 1) <= d2) r++;console.log(r);import { readFileSync } from "node:fs";const [x1, y1, x2, y2]: number[] = readFileSync(0, "utf8").trim().split(" ").map(Number); const dx: number = Math.abs(x2 - x1); // 3const dy: number = Math.abs(y2 - y1); // 1 - 7 = -6 → 6console.log(dx, dy); const d2: number = dx ** 2 + dy ** 2; // 45const dist: number = Math.sqrt(d2); // 6.708203932499369console.log(dist.toFixed(2)); // 정수 제곱근: r * r <= d2인 가장 큰 정수let r: number = Math.floor(dist);while (r * r > d2) r--;while ((r + 1) * (r + 1) <= d2) r++;console.log(r);import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int x1 = in.nextInt(), y1 = in.nextInt(), x2 = in.nextInt(), y2 = in.nextInt(); int dx = Math.abs(x2 - x1); // 3 int dy = Math.abs(y2 - y1); // 1 - 7 = -6 → 6 System.out.println(dx + " " + dy); int d2 = (int) (Math.pow(dx, 2) + Math.pow(dy, 2)); // pow는 double을 돌려줘요 double dist = Math.sqrt(d2); // 6.708203932499369 System.out.println(String.format("%.2f", dist)); // 정수 제곱근: r * r <= d2인 가장 큰 정수 int r = (int) dist; while (r * r > d2) r--; while ((r + 1) * (r + 1) <= d2) r++; System.out.println(r); }}using System; class Program{ static void Main() { string[] p = Console.ReadLine().Split(' '); int x1 = int.Parse(p[0]), y1 = int.Parse(p[1]), x2 = int.Parse(p[2]), y2 = int.Parse(p[3]); int dx = Math.Abs(x2 - x1); // 3 int dy = Math.Abs(y2 - y1); // 1 - 7 = -6 → 6 Console.WriteLine($"{dx} {dy}"); int d2 = (int)(Math.Pow(dx, 2) + Math.Pow(dy, 2)); // Pow는 double을 돌려줘요 double dist = Math.Sqrt(d2); // 6.708203932499369 Console.WriteLine(dist.ToString("F2")); // 정수 제곱근: r * r <= d2인 가장 큰 정수 int r = (int)dist; while (r * r > d2) r--; while ((r + 1) * (r + 1) <= d2) r++; Console.WriteLine(r); }}#include <cmath>#include <iomanip>#include <iostream> int main() { int x1, y1, x2, y2; std::cin >> x1 >> y1 >> x2 >> y2; int dx = std::abs(x2 - x1); // 3 int dy = std::abs(y2 - y1); // 1 - 7 = -6 → 6 std::cout << dx << ' ' << dy << '\n'; int d2 = (int)(std::pow(dx, 2) + std::pow(dy, 2)); // pow는 double을 돌려줘요 double dist = std::sqrt(d2); // 6.708203932499369 std::cout << std::fixed << std::setprecision(2) << dist << '\n'; // 정수 제곱근: r * r <= d2인 가장 큰 정수 int r = (int)dist; while (r * r > d2) r--; while ((r + 1) * (r + 1) <= d2) r++; std::cout << r << '\n';}#include <stdio.h>#include <stdlib.h>#include <math.h> int main(void) { int x1, y1, x2, y2; scanf("%d %d %d %d", &x1, &y1, &x2, &y2); int dx = abs(x2 - x1); /* 3 — abs는 정수용, 실수는 fabs */ int dy = abs(y2 - y1); /* 1 - 7 = -6 → 6 */ printf("%d %d\n", dx, dy); int d2 = (int)(pow(dx, 2) + pow(dy, 2)); /* pow는 double을 돌려줘요 */ double dist = sqrt(d2); /* 6.708203932499369 */ printf("%.2f\n", dist); /* 정수 제곱근: r * r <= d2인 가장 큰 정수 */ int r = (int)dist; while (r * r > d2) r--; while ((r + 1) * (r + 1) <= d2) r++; printf("%d\n", r); return 0;}p = 控制台.读数字();x1 = p[0];y1 = p[1];x2 = p[2];y2 = p[3]; dx = 数学.绝对值(x2 - x1); // 3dy = 数学.绝对值(y2 - y1); // 1 - 7 = -6 → 6控制台.输出(dx + " " + dy); d2 = dx * dx + dy * dy; // 거듭제곱 함수가 없어서 곱해요dist = 数学.平方根(d2); // 6.708203932控制台.输出(字符.保留小数(dist, 2)); // 정수 제곱근: r * r <= d2인 가장 큰 정수r = 数学.向下取整(dist);当 (r * r > d2){ r--;}当 ((r + 1) * (r + 1) <= d2){ r++;}控制台.输出(r);2 7 5 1
3 6 6.71 6
要点
- 绝对值是去掉符号后的大小。
求两个值相隔多远时用它:不知道哪个更大也没关系,abs(a - b)就行。1 - 7是 -6,但相隔的格数是 6。 - 乘方是把同一个数连乘多次。
Python 和 JavaScript 有**运算符,其他语言用pow函数。^在这些语言里都不是乘方。 - 乘方的结果是整数还是浮点数,各语言不同。
Python 的2 ** 10是整数1024。
Java、C#、C、C++ 的pow即使传入整数也返回浮点数(double),要存进整数变量必须先转换。
求平方时写x * x仍是整数,更简单。 - 平方根是平方后等于该数的值。
sqrt总是返回浮点数。
45 的平方根是6.708203932499369,就连能开尽的 25,在 Python 和 Java 里也输出5.0。 - 整数平方根是满足
r * r <= n的最大整数r。
先把sqrt的结果去掉小数,再乘回去检查并调整。
例子里的两行while就是这个检查。 - 负数的平方根得不到一个数。
Python 会以ValueError停下,其他语言返回NaN(不是数)并继续运行。
各语言的写法
| Python | abs(x)、x ** 2、math.sqrt(x)(import math)。整数平方根用 math.isqrt(n) 直接求出。2 64 也是精确的整数,2 -1 是 0.5。-2 2 是 -4,所以要写 (-2) 2 |
|---|---|
| JavaScript | Math.abs(x)、x ** 2(或 Math.pow(x, 2))、Math.sqrt(x),去掉小数用 Math.floor(...)。-2 2 是 SyntaxError,所以要写 (-2) 2 |
| TypeScript | 和 JavaScript 一样。 结果都是 number |
| Java | Math.abs(x)、Math.pow(x, 2)、Math.sqrt(x)。Math.pow(2, 10) 是 1024.0。int p = Math.pow(2, 10); 是编译错误,要写成 (int) Math.pow(2, 10) |
| C# | Math.Abs(x)、Math.Pow(x, 2)、Math.Sqrt(x)。Math.Pow 是 double,所以 int p = Math.Pow(2, 10); 是错误 CS0266。要写成 (int)Math.Pow(2, 10) |
| C++ | <cmath> 里的 std::abs(x)、std::pow(x, 2)、std::sqrt(x)。std::pow 是 double,所以 std::cout << std::pow(10, 6) 输出 1e+06。要输出整数就用 (long long) 转换 |
| C | 整数的绝对值用 <stdlib.h> 的 abs(x),浮点数的绝对值用 <math.h> 的 fabs(x)。pow(x, 2) 和 sqrt(x) 也在 <math.h> 里,返回 double |
| Craykon | 数学.绝对值(x)、数学.平方根(x),去掉小数用 数学.向下取整(...)。乘方既没有运算符也没有函数,所以像 x * x 这样相乘。2 ^ 10 和 2 ** 10 是编译错误 |
常见错误
想用 ^ 做乘方Python · JavaScript · TypeScript · Java · C# · C · C++
System.out.println(2 ^ 10);→ 8 — 不是 1024。^ 是另一种运算,比较两个数的二进制位(异或)。不会报错,只是得到一个莫名其妙的数
Python 和 JavaScript 写 2 ** 10,其他语言用 pow 函数(Java 是 (int) Math.pow(2, 10))。
Craykon 会把 ^ 报为编译错误。
把 sqrt 取整后的值当成整数平方根
const x = 9007199136250224; // 94906265 * 94906265 - 1
console.log(Math.floor(Math.sqrt(x)));→ 94906265 — 可是 94906265 的平方比 x 大 1。正确答案是 94906264。数很大时 sqrt 的结果被舍入,正好落在 94906265 上。8 种语言都得到同样的值
取整之后乘回去检查:加上 while (r * r > x) r--; 就得到 94906264。
Python 的 math.isqrt(x) 给出精确答案。
对浮点数用了整数版的 absC · C++
double d = -2.7;
printf("%d\n", abs(d));→ 2 — 不是 2.7。<stdlib.h> 的 abs 是给整数用的,所以小数部分先被丢掉了。在 C++ 里不写 std:: 而写 abs(d),结果也是 2
C 用 fabs(d),C++ 用 std::abs(d)。
两者都是 2.7。
各语言的差异
答案必须是整数的题目,尽量在整数范围内计算。
要问距离是否不超过
只有真正需要平方根时才用
乘方也一样。
在 Java、C#、C、C++ 里需要很大的整数乘方时,就在
答案必须是整数的题目,尽量在整数范围内计算。
要问距离是否不超过
r,不用 sqrt,而是像 dx * dx + dy * dy <= r * r 这样比较平方,就不会有浮点数掺进来。只有真正需要平方根时才用
sqrt,转换成整数后再乘一次检查。乘方也一样。
pow 用浮点数计算,数非常大时末位会出错:3 连乘 34 次是 16677181699666569,而 Java 的 (long) Math.pow(3, 34) 得到 16677181699666568。在 Java、C#、C、C++ 里需要很大的整数乘方时,就在
long(long long)变量上一次一次地乘。