区间算数

区间算数

内容基于 SICP ,练习 2.7-2.16。

引言

区间算数是指一套定义在区间上的运算。用区间表示不确定量的取值范围,常用来衡量测量值的误差,如 35.6±0.2°C。带有不确定性的量参与运算,结果也是不确定量,区间算数就是用来形式化这样的运算。本文只涉及四则运算,讨论其数学原理、scheme 实现和潜在的问题。

数学原理

区间的存储结构是上下界组成的序对,如果结果的界是运算数的界的函数,编程会比较方便。

下面着重讨论结果的界是不是运算数的函数,是什么函数。记,
x ∈ [ l x , u x ] y ∈ [ l y , u y ] z = f ( x , y ) , f ∈ { + , − , × , ÷ } x\in[l_x,u_x]\\ y\in[l_y,u_y]\\ z=f(x,y),f\in\{+,-,\times,\div\} x[lx,ux]y[ly,uy]z=f(x,y),f{+,,×,÷}
以区间 x x x为横轴, y y y为纵轴, z z z为竖轴绘图。从 z z z轴俯视:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kX8cd6Wv-1580474216509)(D:\home\Document\区间算数\区间算数.png)]

四个顶点处的 z z z值是我们容易求出的。下面结合图像分析四则运算。

z = x + y z=x+y z=x+y,绘制其图像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jUIWfFp-1580474216511)(D:\home\Document\区间算数\add.png)]

可以看出,最值点在立方体的/对角线上。左下最低,右上最高(将+应用到相应序对上):
z ∈ [ l x + l y , u x + u y ] z\in[l_x+l_y,u_x+u_y] z[lx+ly,ux+uy]
想象一个在 z z z平面上滑动的矩形,可以想象到,上述结论对所有可能的区间都成立,与区间的相对位置、区间的正负均无关。

z = x − y z=x-y z=xy,绘图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GFQNdgBw-1580474216512)(D:\home\Document\区间算数\sub.png)]

可以看出,和加法类似,减法的最值点出现在\对角线上。左上最低,右下最高,运用减法在相应的序对上:
z ∈ [ l x − u y , u x − l y ] z\in [l_x-u_y,u_x-l_y] z[lxuy,uxly]
与相对位置、正负无关。

乘法比较特殊,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKGpv3be-1580474216512)(D:\home\Document\区间算数\mul.png)]

想象滑动的矩形,至少有4中可能的最值情况(矩形分别在四个象限内)。但由于平面内没有局部极值点,最值一定在4个顶点中,即,
P = { l x l y , l x u y , u x l y , u x u y } z ∈ [ min ⁡ { P } , max ⁡ { P } ] P=\{l_xl_y,l_xu_y,u_xl_y,u_xu_y\}\\ z\in[\min\{P\},\max\{P\}] P={lxly,lxuy,uxly,uxuy}z[min{P},max{P}]

3D图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IhXXMVW1-1580474216513)(D:\home\Document\区间算数\div.png)]

总体来说和乘法类似,但在除数为零时趋于无穷。这样的除法会产生开区间,突破从闭区间到闭区间封闭运算的语法前提,同时有“不确定性无穷大”这个语义上的困难。

将除法转化为乘法可以让我们看得更清晰一些:
z = x y = x 1 y z=\frac{x}{y}=x\frac{1}{y} z=yx=xy1
问题转化为,已知区间 y y y,求 1 y \frac{1}{y} y1.

绘制 z = 1 y z=\frac{1}{y} z=y1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wippwPJ8-1580474216514)(D:\home\Document\区间算数\frac1y.png)]

图中用红、橙、绿表示出三个区间,分别属于正区间、跨零区间、负区间。对于正区间和负区间都有,
z ∈ [ 1 u , 1 l ] z\in[\frac{1}{u},\frac{1}{l}] z[u1,l1]
但对于跨零区间这个结果已经不能代表不确定性,程序应在用户除以跨零区间时发出警告。

Scheme 实现

#lang sicp

(define (make-interval a b)
  (cons a b))

(define (upper-bound interval)
  (cdr interval))

(define (lower-bound interval)
  (car interval))


(define (add-interval x y)
  (make-interval (+ (lower-bound x) (lower-bound y))
                 (+ (upper-bound x) (upper-bound y))))

(define (mul-interval x y)
  (let ((p1 (* (lower-bound x) (lower-bound y)))
        (p2 (* (lower-bound x) (upper-bound y)))
        (p3 (* (upper-bound x) (lower-bound y)))
        (p4 (* (upper-bound x) (upper-bound y))))
    (make-interval (min p1 p2 p3 p4)
                   (max p1 p2 p3 p4))))

(define (div-interval x y)
  (mul-interval x
                (make-interval (/ 1.0 (upper-bound y))
                               (/ 1.0 (lower-bound y)))))

(define (sub-interval x y)
  (make-interval (- (lower-bound x) (upper-bound y))
                 (- (upper-bound x) (lower-bound y))))


; 对百分比表示的支持
(define (make-center-width c w)
  (make-interval (- c w) (+ c w)))

(define (center i)
  (/ (+ (lower-bound i) (upper-bound i)) 2))

(define (width i)
  (/ (- (upper-bound i) (lower-bound i)) 2))


(define (make-center-percent c p)
  (make-center-width c (* c p)))

(define (percent i)
  (/ (width i) (center i)))

潜在问题

使用本文的算法,在代数上等价的公式可能算出不同的结果!

例如电阻并联公式,
R = R 1 R 2 R 1 + R 2 = 1 1 R 1 + 1 R 2 R=\frac{R_1R_2}{R_1+R_2}=\frac{1}{\frac{1}{R_1}+\frac{1}{R_2}} R=R1+R2R1R2=R11+R211
对于同样不确定的 R 1 R_1 R1, R 2 R_2 R2右式得出区间比左式窄。原因是左式中不确定量 R 1 R_1 R1 R 2 R_2 R2都有重复出现,让同一份不确定性被计算了两次。一个更极端的例子能更清晰地展示这种现象,对区间 x x x计算
x x \frac{x}{x} xx
从语义上说,分子与分母代表同一不确定量,其商应精确地为1,但本文的算法没有考虑同名变量相等:

> (let ((x (make-interval 5 10)))
    (div-interval x x))
(0.5 . 2.0)

但可以肯定的是,本算法给出的区间一定包含考虑同名变量的算法给出的区间。

附录

下面的代码摘自实验用jupyter notebook,用于探索乘法时最值点出现的情况以及乘法相对误差的传播规律,未经整理,供参考。

python 草稿

#!/usr/bin/env python
# coding: utf-8

# In[10]:


from random import randint
from itertools import *
from collections import Counter


# In[4]:


def p_gen():
    a = randint(1, 100)
    b = randint(a, 100)
    return a, b

def n_gen():
    a, b = p_gen()
    return -b, -a

def z_gen():
    return randint(-100, 0), randint(0, 100)


# In[8]:


p_intervals = [p_gen() for i in range(100)]
n_intervals = [n_gen() for i in range(100)]
z_intervals = [z_gen() for i in range(100)]


# In[29]:


case_pz = []
for p_interval, z_interval in product(p_intervals, z_intervals):
    bounds = [p_bound * z_bound for p_bound, z_bound in product(p_interval, z_interval)]
    maxi = max(range(len(bounds)), key=lambda x: bounds[x])
    mini = min(range(len(bounds)), key=lambda x: bounds[x])
    cases.append(((p_interval, z_interval), mini, maxi))


# In[30]:


[case[0] for  case in cases if case[1:] == (0, 1)]


# In[31]:


cnt = Counter(case[1:] for case in cases)


# In[ ]:


case_pn = []
for p_interval, n_interval in product(p_intervals, z_intervals):
    bounds = [p_bound * z_bound for p_bound, z_bound in product(p_interval, z_interval)]
    maxi = max(range(len(bounds)), key=lambda x: bounds[x])
    mini = min(range(len(bounds)), key=lambda x: bounds[x])
    cases.append(((p_interval, z_interval), mini, maxi))


# In[39]:


def test(left_intervals, right_intervals):
    cases = []
    for interval1, interval2 in product(left_intervals, right_intervals):
        bounds = [bound1 * bound2 for bound1, bound2 in product(interval1, interval2)]
        maxi = max(range(len(bounds)), key=lambda x: bounds[x])
        mini = min(range(len(bounds)), key=lambda x: bounds[x])
        cases.append(((interval1, interval2), mini, maxi))
    return cases

def count(cases):
    return Counter(case[1:] for case in cases)


# In[42]:


count(test(p_intervals, z_intervals))


# In[43]:


count(test(p_intervals, n_intervals))


# In[44]:


case_pn = test(p_intervals, n_intervals)


# In[45]:


cnt_pn = count(case_pn)


# In[49]:


[case[0] for case in case_pn if case[1:] == (0, 0)]


# In[50]:


count(test(z_intervals, p_intervals))


# In[51]:


count(test(n_intervals, p_intervals))


# In[55]:


for intervals1, intervals2 in combinations_with_replacement((p_intervals, z_intervals, n_intervals), 2):
    print('-' * 30)
    print(count(test(intervals1, intervals2)))
    print(count(test(intervals2, intervals1)))
    print('-' * 30)


# In[57]:


len(list(combinations_with_replacement(range(3), 2)))


# In[58]:


def percent(interval):
    a, b = interval
    return (b - a) / (b + a)


# In[59]:


def mul(i1, i2):
    return i1[0] * i2[0], i1[1] * i2[1]


# In[63]:


errors = []
for i1, i2 in product(p_intervals, repeat=2):
    # print('%.4f  %.4f  %.4f --- %.4f' % (p1:=percent(i1), p2:=percent(i2), p1 + p2, percent(mul(i1, i2))))
    errors.append(percent(i1) + percent(i2) - percent(mul(i1, i2)))


# In[66]:


(sum(i ** 2 for i in errors) / len(errors)) ** .5

errors = []
for i1, i2 in product(p_intervals, repeat=2):
    # print('%.4f  %.4f  %.4f --- %.4f' % (p1:=percent(i1), p2:=percent(i2), p1 + p2, percent(mul(i1, i2))))
    errors.append(percent(i1) + percent(i2) - percent(mul(i1, i2)))


# In[66]:


(sum(i ** 2 for i in errors) / len(errors)) ** .5

可见,

  • 乘法最值分布情况复杂,我们不如直接在4个顶点中选最值
  • 按百分比度量区间长度时, p e r c e n t z ≈ p e r c e n t x + p e r c e n t y percent_z\approx percent_x+percent_y percentzpercentx+percenty
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验一 算数编码实验报告 班级:11电信实验班 姓名:颜妮 学号:2011339960109 一、实验目的 掌握算数编码原理。 二、实验内容 利用Matlab编写程序实现算数编码,包括: 1. 对文件符号进行概率统计,生成编码表; 2. 对文件进行压缩编码; 3. (选做)对文件进行解压缩,比较原始据和解压后的据之间是否有损耗。 三、实验仪器 1、计算机一台; 2、Matlab仿真软件。 实验原理 算术编码的编码对象是一则消息或一个字符序列,其编码思路是将该消息或字符序列 表示成0和1之间的一个间隔(Interval)上的一个浮点小。 在进行算术编码之前,需要对字符序列中每个字符的出现概率进行统计,根据各字符 出现概率的大小,将每个字符映射到[0,1]区间上的某个子区间中。然后,再利用递归算 法,将整个字符序列映射到[0,1]区间上的某个Interval中。在进行编码时,只需从该I nterval中任选一个小,将其转化为二进制。 符号序列越长,编码表示它的Interval的间隔就越小,表示这一间隔所需的二进制位 就越多,编码输出的码字就越长。 实验内容 clear; clc; %编码 x='state_tree'; range_low=[0.6 0.7 0.1 0.7 0.2 0 0.7 0.5 0.2 0.2]; range_high=[0.7 1.0 0.2 1.0 0.5 0.1 1.0 0.6 0.5 0.5]; low=0;high=0;range_length=1; for i=1:length(x) next_low=low+range_length*range_low(i); next_high=low+range_length*range_high(i); low=next_low;high=next_high;range_length=next_high-next_low; end vpa(low,10);vpa(high,10); number=vpa(rand*(vpa(high,10)-vpa(low,10))+vpa(low,10),10) %解码 string=['_' 'a' 'e' 'r' 's' 't']; low=[0 0.1 0.2 0.5 0.6 0.7]; high=[0.1 0.2 0.5 0.6 0.7 1.0]; for i=1:10 for j=1:6 if low(j)<=number&&number<=high(j) str(i)=num2str(string(j)); number=(number-low(j))/(high(j)-low(j)); break; end end end disp(str); 实验心得 此次实验运用的是相对熟悉的MATLAB软件,无需多余的学习,主要收获的是有关于编 码和解码的思想,掌握了编码的原理。过程中遇到的问题主要是vpa函起初不是很了解 ,后来经查找和运用,目前可以掌握。 ----------------------- 实验一--算数编码实验报告全文共3页,当前为第1页。 实验一--算数编码实验报告全文共3页,当前为第2页。 实验一--算数编码实验报告全文共3页,当前为第3页。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值