Go基础:常用数学函数处理(主要是math包rand包的处理)

文章目录

    • [一、`math` 包概览](#一、math 包概览)
      • [1.1 重要数学常量](#1.1 重要数学常量)
      • [1.2 基本四则运算与取整](#1.2 基本四则运算与取整)
      • [1.3 指数与对数函数](#1.3 指数与对数函数)
      • [1.4 三角函数](#1.4 三角函数)
      • [1.5 其他实用函数](#1.5 其他实用函数)
      • [1.6 浮点数处理的特殊函数](#1.6 浮点数处理的特殊函数)
    • 二、随机数
      • [2.1 math/rand 包详解](#2.1 math/rand 包详解)
      • [2.2 crypto/rand 包详解](#2.2 crypto/rand 包详解)

一、math 包概览

Go 语言通过内置的 math 包提供了丰富的数学运算功能,它提供了一套完整、高效且标准的数学工具。这些函数都经过高度优化,性能卓越,适用于科学计算、图形学、游戏开发等众多领域。

1.1 重要数学常量

math 包中定义了许多数学常数,这些常量在科学和工程计算中非常有用。

常量 描述 近似值
math.Pi 圆周率 π 3.141592653589793
math.E 自然对数的底数 e 2.718281828459045
math.Phi 黄金比例 φ 1.618033988749895
math.Sqrt2 2 的平方根 1.4142135623730951
math.SqrtE e 的平方根 1.6487212707001282
math.SqrtPi π 的平方根 1.772453850905516
math.Ln2 2 的自然对数 0.6931471805599453
math.Ln10 10 的自然对数 2.302585092994046

示例:使用数学常量

go 复制代码
package main
import (
	"fmt"
	"math"
)
func main() {
	// 计算一个半径为 5 的圆的周长和面积
	radius := 5.0
	circumference := 2 * math.Pi * radius
	area := math.Pi * radius * radius
	fmt.Printf("半径为 %.2f 的圆:\n", radius)
	fmt.Printf("周长: %.4f\n", circumference) // 输出: 周长: 31.4159
	fmt.Printf("面积: %.4f\n", area)         // 输出: 面积: 78.5398
	// 使用自然对数底 e
	fmt.Printf("自然对数的底数 e: %.10f\n", math.E) // 输出: 自然对数的底数 e: 2.7182818285
}

1.2 基本四则运算与取整

虽然 Go 的基本运算符 +, -, *, / 可以完成四则运算,但 math 包提供了更复杂的取整功能。

函数 描述 示例
math.Ceil(x) 向上取整,返回不小于 x 的最小整数。 math.Ceil(1.2) -> 2.0
math.Floor(x) 向下取整,返回不大于 x 的最大整数。 math.Floor(1.8) -> 1.0
math.Round(x) 四舍五入到最近的整数。如果小数部分正好是 0.5,则向偶数方向舍入。 math.Round(1.5) -> 2.0, math.Round(2.5) -> 2.0
math.Trunc(x) 截断小数部分,返回整数部分。 math.Trunc(1.8) -> 1.0

案例代码:取整函数对比

go 复制代码
package main
import (
	"fmt"
	"math"
)
func main() {
	x := 3.14159
	y := -2.718
	z := 2.5 // 用于测试 Round 的特殊情况
	fmt.Printf("原始值: x = %.5f, y = %.5f, z = %.1f\n", x, y, z)
	fmt.Println("--- 向上取整 ---")
	fmt.Printf("math.Ceil(%.5f) = %.1f\n", x, math.Ceil(x)) // 4.0
	fmt.Printf("math.Ceil(%.5f) = %.1f\n", y, math.Ceil(y)) // -2.0
	fmt.Println("--- 向下取整 ---")
	fmt.Printf("math.Floor(%.5f) = %.1f\n", x, math.Floor(x)) // 3.0
	fmt.Printf("math.Floor(%.5f) = %.1f\n", y, math.Floor(y)) // -3.0
	fmt.Println("--- 四舍五入 ---")
	fmt.Printf("math.Round(%.5f) = %.1f\n", x, math.Round(x)) // 3.0
	fmt.Printf("math.Round(%.5f) = %.1f\n", y, math.Round(y)) // -3.0
	fmt.Printf("math.Round(%.1f) = %.1f\n", z, math.Round(z)) // 2.0 (向偶数舍入)
	fmt.Println("--- 截断 ---")
	fmt.Printf("math.Trunc(%.5f) = %.1f\n", x, math.Trunc(x)) // 3.0
	fmt.Printf("math.Trunc(%.5f) = %.1f\n", y, math.Trunc(y)) // -2.0
}

1.3 指数与对数函数

函数 描述 示例
math.Pow(x, y) 返回 x 的 y 次幂。 math.Pow(2, 3) -> 8.0
math.Pow10(n) 返回 10 的 n 次幂。 math.Pow10(2) -> 100.0
math.Sqrt(x) 返回 x 的平方根。 math.Sqrt(16) -> 4.0
math.Cbrt(x) 返回 x 的立方根。 math.Cbrt(27) -> 3.0
math.Log(x) 返回 x 的自然对数(底为 e)。 math.Log(math.E) -> 1.0
math.Log10(x) 返回 x 的常用对数(底为 10)。 math.Log10(100) -> 2.0
math.Log2(x) 返回 x 的二进制对数(底为 2)。 math.Log2(8) -> 3.0
math.Exp(x) 返回 e 的 x 次幂。 math.Exp(1) -> 2.718...

案例代码:计算勾股定理和连续复利

go 复制代码
package main
import (
	"fmt"
	"math"
)
func main() {
	// 1. 计算直角三角形的斜边 (勾股定理: a² + b² = c²)
	a, b := 3.0, 4.0
	c_squared := math.Pow(a, 2) + math.Pow(b, 2)
	c := math.Sqrt(c_squared)
	fmt.Printf("直角边为 %.1f 和 %.1f 的斜边长度为: %.1f\n", a, b, c) // 输出: 5.0
	// 2. 计算连续复利 (公式: P * e^(r*t))
	principal := 1000.0 // 本金
	rate := 0.05        // 年利率
	time := 10.0        // 年数
	amount := principal * math.Exp(rate*time)
	fmt.Printf("本金 %.2f, 年利率 %.2f, %d 年后的连续复利本息和为: %.2f\n", principal, rate, int(time), amount) // 输出: 1648.72
}

1.4 三角函数

所有三角函数的参数单位都是弧度 ,而不是角度。你可以使用 math.Pi 进行角度和弧度的转换。

  • 角度转弧度 : radians = degrees * math.Pi / 180
  • 弧度转角度 : degrees = radians * 180 / math.Pi
函数 描述
math.Sin(x) 正弦函数
math.Cos(x) 余弦函数
math.Tan(x) 正切函数
math.Asin(x) 反正弦函数
math.Acos(x) 反余弦函数
math.Atan(x) 反正切函数
math.Atan2(y, x) 返回 y/x 的反正切值,但它会利用两个参数的符号来确定结果的象限,比 Atan 更准确。

案例代码:计算一个 30 度角的三角函数值

go 复制代码
package main
import (
	"fmt"
	"math"
)
func main() {
	angleDegrees := 30.0
	// 将角度转换为弧度
	angleRadians := angleDegrees * math.Pi / 180
	sinVal := math.Sin(angleRadians)
	cosVal := math.Cos(angleRadians)
	tanVal := math.Tan(angleRadians)
	fmt.Printf("角度 %.2f 度的三角函数值:\n", angleDegrees)
	fmt.Printf("Sin(%.2f°) = %.4f\n", angleDegrees, sinVal) // 0.5000
	fmt.Printf("Cos(%.2f°) = %.4f\n", angleDegrees, cosVal) // 0.8660
	fmt.Printf("Tan(%.2f°) = %.4f\n", angleDegrees, tanVal) // 0.5774
}

1.5 其他实用函数

函数 描述 示例
math.Abs(x) 返回 x 的绝对值。 math.Abs(-10) -> 10.0
math.Max(x, y) 返回 x 和 y 中的较大值。 math.Max(1, 2) -> 2.0
math.Min(x, y) 返回 x 和 y 中的较小值。 math.Min(1, 2) -> 1.0
math.Mod(x, y) 取余运算,返回 x/y 的浮点余数。 math.Mod(10, 3) -> 1.0
math.Dim(x, y) 返回 x - y 的最大值与 0 之间的数,即 max(x-y, 0) math.Dim(5, 10) -> 0.0

案例代码:在一组数字中找到最大值、最小值和绝对值

go 复制代码
package main
import (
	"fmt"
	"math"
)
func main() {
	numbers := []float64{-42, 3.14, 0, 98.6, -17.5}
	if len(numbers) == 0 {
		fmt.Println("切片为空")
		return
	}
	// 初始化最大值和最小值为切片的第一个元素
	maxVal := numbers[0]
	minVal := numbers[0]
	// 遍历切片,使用 math.Max 和 math.Min
	for _, num := range numbers[1:] {
		maxVal = math.Max(maxVal, num)
		minVal = math.Min(minVal, num)
	}
	fmt.Printf("数字切片: %v\n", numbers)
	fmt.Printf("最大值: %.2f\n", maxVal) // 98.60
	fmt.Printf("最小值: %.2f\n", minVal) // -42.00
	// 计算 -42 的绝对值
	absVal := math.Abs(numbers[0])
	fmt.Printf("第一个元素 %.2f 的绝对值是: %.2f\n", numbers[0], absVal) // 42.00
}

1.6 浮点数处理的特殊函数

在浮点数运算中,可能会遇到一些特殊值,如"非数字"(Not a Number, NaN)和无穷大(Infinity)。math 包提供了处理这些情况的函数。

函数/常量 描述
math.NaN() 返回一个"非数字"值。
math.Inf(sign) 如果 sign >= 0,返回正无穷大;否则返回负无穷大。
math.IsNaN(x) 判断 x 是否为 NaN。
math.IsInf(x, sign) 判断 x 是否为无穷大。sign 的含义同 math.Inf

案例代码:处理特殊浮点值

go 复制代码
package main
import (
	"fmt"
	"math"
)
func main() {
	// 生成特殊值
	nan := math.NaN()
	posInf := math.Inf(1)
	negInf := math.Inf(-1)
	fmt.Printf("NaN: %v, 正无穷: %v, 负无穷: %v\n", nan, posInf, negInf)
	// 检查特殊值
	fmt.Printf("nan 是否为 NaN? %t\n", math.IsNaN(nan)) // true
	fmt.Printf("posInf 是否为无穷大? %t\n", math.IsInf(posInf, 1)) // true
	// 包含 NaN 的运算
	result := posInf + negInf
	fmt.Printf("正无穷 + 负无穷 = %v\n", result) // NaN
	fmt.Printf("result 是否为 NaN? %t\n", math.IsNaN(result)) // true
	// 重要:NaN 与任何值(包括它自己)比较都返回 false
	fmt.Printf("nan == nan? %t\n", nan == nan) // false
	// 正确的判断方式是使用 math.IsNaN()
}

二、随机数

Go 语言中处理随机数主要涉及两个核心包:

  • math/rand:用于生成伪随机数。它速度快,适用于大多数非密码学安全的场景,如游戏、模拟、抽样等。
  • crypto/rand:用于生成密码学安全的随机数。它速度较慢,但随机性更高,不可预测,适用于生成密钥、盐值、会话令牌等安全敏感的场景。

2.1 math/rand 包详解

math/rand 是我们日常开发中最常用的随机数包。让我们从最基础的例子开始。

案例:模拟掷骰子

go 复制代码
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	// 为了兼容性或明确性,仍然可以手动播种
	rand.Seed(time.Now().UnixNano())

	// 掷一个6面的骰子,结果范围是 [1, 6]
	// rand.Intn(6) 生成 [0, 5],所以我们 +1
	diceRoll := rand.Intn(6) + 1
	fmt.Printf("你掷出了: %d\n", diceRoll)

	// 模拟掷10次骰子
	fmt.Println("连续掷10次骰子:")
	for i := 0; i < 10; i++ {
		fmt.Print(rand.Intn(6)+1, " ")
	}
	fmt.Println()
}

案例:随机抽奖

go 复制代码
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())

	prizes := []string{"一等奖", "二等奖", "三等奖", "谢谢参与", "优惠券"}

	// 随机选择一个索引,范围是 [0, len(prizes))
	randomIndex := rand.Intn(len(prizes))
	winner := prizes[randomIndex]

	fmt.Printf("恭喜你,抽中了: %s\n", winner)
}

2.2 crypto/rand 包详解

当你需要生成无法被预测的随机数时,crypto/rand 是唯一的选择。它的用法与 math/rand 完全不同,它不提供像 Intn() 或 Float64() 这样的便捷函数,而是专注于生成原始的随机字节流。

案例:生成一个16字节(128位)的随机密钥

go 复制代码
package main

import (
	"crypto/rand"
	"fmt"
)

func main() {
	key := make([]byte, 16) // 创建一个16字节的切片

	// 从 crypto/rand 读取随机数填充到 key 切片中
	// Read 是一个阻塞操作,它会等待系统收集到足够的熵
	n, err := rand.Read(key)
	if err != nil {
		// 在某些系统上(如某些 WebAssembly 环境或无熵源的容器),可能会失败
		fmt.Println("生成随机数失败:", err)
		return
	}

	fmt.Printf("成功读取了 %d 字节的随机数\n", n)
	fmt.Printf("生成的随机密钥 (十六进制): %x\n", key)
}
相关推荐
学习同学2 小时前
从0到1制作一个go语言服务器 (一) 配置
服务器·开发语言·golang
大飞pkz2 小时前
【设计模式】桥接模式
开发语言·设计模式·c#·桥接模式
期待のcode2 小时前
MyBatis框架—延迟加载与多级缓存
java·数据库·后端·缓存·mybatis
数据知道2 小时前
Go基础:文件与文件夹操作详解
开发语言·后端·golang·go语言
华仔啊2 小时前
Spring 配置混乱?搞懂这两个核心组件,问题真能少一半
java·后端·spring
珍宝商店2 小时前
原生 JavaScript 方法实战指南
开发语言·前端·javascript
喂完待续2 小时前
【序列晋升】45 Spring Data Elasticsearch 实战:3 个核心方案破解索引管理与复杂查询痛点,告别低效开发
java·后端·spring·big data·spring data·序列晋升
神龙斗士2402 小时前
Java 数组的定义与使用
java·开发语言·数据结构·算法
白露与泡影2 小时前
2025互联网大厂高频Java面试真题解析
java·开发语言·面试