HARMONYOS应用实例270:几何概型演示
·
- 几何概型演示
- 功能:在方形区域内随机撒点,计算落在特定区域内的概率,演示 P=目标面积总面积P = \frac{\text{目标面积}}{\text{总面积}}P=总面积目标面积。
在方形区域内随机撒点
计算落在特定区域内的概率
演示 P = 目标面积/总面积 的几何概型原理
支持多种目标区域形状(圆形、正方形、三角形)
实时显示实验概率和理论概率对比
通过蒙特卡洛方法验证概率理论
- 功能:在方形区域内随机撒点,计算落在特定区域内的概率,演示 P=目标面积总面积P = \frac{\text{目标面积}}{\text{总面积}}P=总面积目标面积。
// 几何概型演示
// 功能:在方形区域内随机撒点,计算落在特定区域内的概率,演示 P = 目标面积/总面积
// 点坐标接口
interface GeoPoint {
x: number;
y: number;
}
// 区域配置接口
interface RegionConfig {
type: 'circle' | 'square' | 'triangle';
centerX: number;
centerY: number;
radius?: number;
side?: number;
color: string;
}
@Entry
@Component
struct GeometricProbability {
@State points: GeoPoint[] = [];
@State totalPoints: number = 0;
@State targetPoints: number = 0;
@State isSimulating: boolean = false;
@State simulationSpeed: number = 10;
@State maxPoints: number = 10000;
@State selectedRegion: string = 'circle';
@State canvasWidth: number = 350;
@State canvasHeight: number = 350;
@State regionConfig: RegionConfig = {
type: 'circle',
centerX: 175,
centerY: 175,
radius: 150,
color: '#E91E63'
};
private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();
private drawCanvas() {
const ctx = this.canvasContext
const width = this.canvasWidth
const height = this.canvasHeight
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = '#F5F5F5'
ctx.fillRect(0, 0, width, height)
ctx.strokeStyle = '#333'
ctx.lineWidth = 2
ctx.strokeRect(0, 0, width, height)
this.drawTargetRegion(ctx)
this.drawPoints(ctx)
this.updateProbability()
}
private drawTargetRegion(ctx: CanvasRenderingContext2D) {
const config = this.regionConfig
ctx.fillStyle = config.color + '33'
ctx.strokeStyle = config.color
ctx.lineWidth = 3
if (config.type === 'circle' && config.radius) {
ctx.beginPath()
ctx.arc(config.centerX, config.centerY, config.radius, 0, 2 * Math.PI)
ctx.fill()
ctx.stroke()
} else if (config.type === 'square' && config.side) {
const halfSide = config.side / 2
ctx.fillRect(config.centerX - halfSide, config.centerY - halfSide, config.side, config.side)
ctx.strokeRect(config.centerX - halfSide, config.centerY - halfSide, config.side, config.side)
} else if (config.type === 'triangle' && config.side) {
const halfSide = config.side / 2
const height = (Math.sqrt(3) / 2) * config.side
ctx.beginPath()
ctx.moveTo(config.centerX, config.centerY - height / 2)
ctx.lineTo(config.centerX - halfSide, config.centerY + height / 2)
ctx.lineTo(config.centerX + halfSide, config.centerY + height / 2)
ctx.closePath()
ctx.fill()
ctx.stroke()
}
}
private drawPoints(ctx: CanvasRenderingContext2D) {
this.points.forEach((point) => {
const isTarget = this.isPointInTargetRegion(point)
ctx.beginPath()
ctx.arc(point.x, point.y, 3, 0, 2 * Math.PI)
ctx.fillStyle = isTarget ? '#4CAF50' : '#2196F3'
ctx.fill()
})
}
private isPointInTargetRegion(point: GeoPoint): boolean {
const config = this.regionConfig
const dx = point.x - config.centerX
const dy = point.y - config.centerY
if (config.type === 'circle' && config.radius) {
return (dx * dx + dy * dy) <= (config.radius * config.radius)
} else if (config.type === 'square' && config.side) {
const halfSide = config.side / 2
return Math.abs(dx) <= halfSide && Math.abs(dy) <= halfSide
} else if (config.type === 'triangle' && config.side) {
const halfSide = config.side / 2
const height = (Math.sqrt(3) / 2) * config.side
const x1 = config.centerX
const y1 = config.centerY - height / 2
const x2 = config.centerX - halfSide
const y2 = config.centerY + height / 2
const x3 = config.centerX + halfSide
const y3 = config.centerY + height / 2
const denominator = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3))
const a = ((y2 - y3) * (point.x - x3) + (x3 - x2) * (point.y - y3)) / denominator
const b = ((y3 - y1) * (point.x - x3) + (x1 - x3) * (point.y - y3)) / denominator
const c = 1 - a - b
return a >= 0 && a <= 1 && b >= 0 && b <= 1 && c >= 0 && c <= 1
}
return false
}
private updateProbability() {
this.targetPoints = this.points.filter(point => this.isPointInTargetRegion(point)).length
this.totalPoints = this.points.length
}
private calculateTheoreticalProbability(): number {
const totalArea = this.canvasWidth * this.canvasHeight
const config = this.regionConfig
let targetArea = 0
if (config.type === 'circle' && config.radius) {
targetArea = Math.PI * config.radius * config.radius
} else if (config.type === 'square' && config.side) {
targetArea = config.side * config.side
} else if (config.type === 'triangle' && config.side) {
const height = (Math.sqrt(3) / 2) * config.side
targetArea = 0.5 * config.side * height
}
return targetArea / totalArea
}
private getErrorValue(): number {
const theoretical = this.calculateTheoreticalProbability()
const experimental = this.totalPoints > 0 ? this.targetPoints / this.totalPoints : 0
return Math.abs(experimental - theoretical)
}
private addRandomPoints(count: number) {
for (let i = 0; i < count; i++) {
const x = Math.random() * this.canvasWidth
const y = Math.random() * this.canvasHeight
this.points.push({ x, y })
}
this.updateProbability()
this.drawCanvas()
}
private startSimulation() {
if (this.isSimulating) return
this.isSimulating = true
this.points = []
this.totalPoints = 0
this.targetPoints = 0
const interval = setInterval(() => {
if (this.totalPoints >= this.maxPoints) {
clearInterval(interval)
this.isSimulating = false
return
}
this.addRandomPoints(this.simulationSpeed)
}, 50)
}
private reset() {
this.isSimulating = false
this.points = []
this.totalPoints = 0
this.targetPoints = 0
this.drawCanvas()
}
private changeRegionType(type: string) {
this.selectedRegion = type
if (type === 'circle') {
this.regionConfig = {
type: 'circle',
centerX: 175,
centerY: 175,
radius: 150,
color: '#E91E63'
}
} else if (type === 'square') {
this.regionConfig = {
type: 'square',
centerX: 175,
centerY: 175,
side: 250,
color: '#9C27B0'
}
} else if (type === 'triangle') {
this.regionConfig = {
type: 'triangle',
centerX: 175,
centerY: 175,
side: 280,
color: '#FF9800'
}
}
this.reset()
}
build() {
Column({ space: 15 }) {
Text('几何概型演示')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#E91E63')
.margin({ top: 10 })
Text('在方形区域内随机撒点,计算落在特定区域内的概率,演示 P = 目标面积/总面积')
.fontSize(14)
.fontColor('#666')
.textAlign(TextAlign.Center)
.padding({ left: 15, right: 15 })
Scroll() {
Column({ space: 15 }) {
Column({ space: 10 }) {
Text('目标区域形状')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Row({ space: 10 }) {
Button('圆形')
.width('33%')
.height(45)
.backgroundColor(this.selectedRegion === 'circle' ? '#E91E63' : '#9E9E9E')
.fontSize(14)
.onClick(() => {
this.changeRegionType('circle')
})
Button('正方形')
.width('33%')
.height(45)
.backgroundColor(this.selectedRegion === 'square' ? '#9C27B0' : '#9E9E9E')
.fontSize(14)
.onClick(() => {
this.changeRegionType('square')
})
Button('三角形')
.width('33%')
.height(45)
.backgroundColor(this.selectedRegion === 'triangle' ? '#FF9800' : '#9E9E9E')
.fontSize(14)
.onClick(() => {
this.changeRegionType('triangle')
})
}
.width('100%')
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 10 }) {
Text('模拟设置')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Column({ space: 5 }) {
Text(`最大点数:${this.maxPoints}`)
.fontSize(14)
.fontColor('#333')
.width('100%')
Slider({
value: this.maxPoints,
min: 1000,
max: 50000,
step: 1000,
style: SliderStyle.OutSet
})
.width('100%')
.blockColor('#E91E63')
.trackColor('#FCE4EC')
.selectedColor('#E91E63')
.showSteps(false)
.onChange((value: number) => {
this.maxPoints = value
})
Text(`模拟速度:${this.simulationSpeed} 点/次`)
.fontSize(14)
.fontColor('#333')
.width('100%')
Slider({
value: this.simulationSpeed,
min: 1,
max: 50,
step: 1,
style: SliderStyle.OutSet
})
.width('100%')
.blockColor('#E91E63')
.trackColor('#FCE4EC')
.selectedColor('#E91E63')
.showSteps(false)
.onChange((value: number) => {
this.simulationSpeed = value
})
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 10 }) {
Text('模拟区域')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Canvas(this.canvasContext)
.width(this.canvasWidth)
.height(this.canvasHeight)
.backgroundColor('#FFFFFF')
.border({ width: 2, color: '#333' })
.borderRadius(4)
.onReady(() => {
this.drawCanvas()
})
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Row({ space: 10 }) {
Button(this.isSimulating ? '模拟中...' : '开始模拟')
.width('50%')
.height(50)
.backgroundColor(this.isSimulating ? '#9E9E9E' : '#E91E63')
.fontSize(16)
.enabled(!this.isSimulating)
.onClick(() => {
this.startSimulation()
})
Button('重置')
.width('50%')
.height(50)
.backgroundColor('#FF9800')
.fontSize(16)
.onClick(() => {
this.reset()
})
}
.width('100%')
Column({ space: 10 }) {
Text('概率统计')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Column({ space: 5 }) {
Row({ space: 10 }) {
Text('总点数:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(this.totalPoints.toString())
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
.layoutWeight(1)
}
.width('100%')
Row({ space: 10 }) {
Text('目标内点数:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(this.targetPoints.toString())
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#4CAF50')
.layoutWeight(1)
}
.width('100%')
Row({ space: 10 }) {
Text('实验概率:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(this.totalPoints > 0 ? (this.targetPoints / this.totalPoints).toFixed(6) : '0')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#E91E63')
.layoutWeight(1)
}
.width('100%')
Row({ space: 10 }) {
Text('理论概率:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(this.calculateTheoreticalProbability().toFixed(6))
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#9C27B0')
.layoutWeight(1)
}
.width('100%')
Row({ space: 10 }) {
Text('误差:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(this.getErrorValue().toFixed(6))
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(this.getErrorValue() < 0.01 ? '#4CAF50' : '#FF5722')
.layoutWeight(1)
}
.width('100%')
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 5 }) {
Text('几何概型原理')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#E91E63')
Text('• 几何概型:如果每个基本事件发生的可能性相同,且样本空间和事件域可以用几何度量(长度、面积、体积)来表示')
.fontSize(12)
.fontColor('#666')
.width('100%')
Text('• 概率公式:P(A) = 目标区域面积 / 总区域面积')
.fontSize(12)
.fontColor('#666')
.width('100%')
Text('• 通过蒙特卡洛方法模拟:随着样本数量增加,实验概率会趋近于理论概率')
.fontSize(12)
.fontColor('#666')
.width('100%')
Text('• 绿色点表示落在目标区域内,蓝色点表示落在目标区域外')
.fontSize(12)
.fontColor('#666')
.width('100%')
}
.width('100%')
.padding(15)
.backgroundColor('#FCE4EC')
.borderRadius(10)
}
.width('100%')
.padding(10)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
}
更多推荐


所有评论(0)