IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    当教室突然安静——扰动,突变与回复

    BaoPinsUi啊发表于 2024-01-29 11:32:04
    love 0

    作为学生,我想各位读者们应该在读书学习的时候多少经历过“原本嘈杂的教室突然变的鸦雀无声”的现象。对此已有相当多的答主做出了定性的解释:

    1.嘈杂的人群说话的空隙偶然重叠会导致音量的突然下降

    2.说话的人会为音量的减小感到警觉而放低说话的音量

    基于这两点,我们可以为嘈杂的人群做一个简单的建模并数值模拟:

    设人群共N人

    f_{n}(t)=g_n(t)*\frac{ln(1+\frac{10}{3}\int_{t-0.3}^{t}F_{n}(t)dt)}{ln(2)}*\frac{1}{2}(1+tanh(\color{red}{m}*[10(\int^{t}_{t-0.25}F_n(t)dt-\int^{t-0.5}_{t-0.75}F_n(t)dt)+\color{red}{n}]))+0.001g_n(t)

    F_n=S_n(f_1,f_2,……f_N)

    其中 f_n(t) 代表编号为n的人在t时刻发出的声响(类似于分贝,人在正常范围对"声响"的感知是线性的), F_n(t) 则是其听到的声响。 在正常的嘈杂环境下二者的均值为1。 f_n(t) 的第二部分 \frac{ln(1+\frac{10}{3}\int_{t-0.3}^{t}F_{n}(t)dt)}{ln(2)} 代表了一小段时间(此处为0.3s)内环境音量平均大小对其说话声音的影响,采用 \frac{ln(1+x)}{ln(2)} 而非正比的形式可以使得 F 与 f 稳定在不动点1附近,在正常情况下不过大或过小,而在发生突变至0后能自行脱离零点回复至1。第三部分 \frac{1}{2}(1+tanh(\color{red}{m}*[10(\int^{t}_{t-0.25}F_n(t)dt-\int^{t-0.5}_{t-0.75}F_n(t)dt)+\color{red}{n}])) 描述了声音的变化对其影响,标红的m、n都是可调的参数,n代表了引起人警觉的中心阈值,而2/m代表这一“警觉”过渡的“宽度”。当声音在半秒内的平均变化率 10(\int^{t}_{t-0.25}F_n(t)dt-\int^{t-0.5}_{t-0.75}F_n(t)dt) 大于-n+1/m时不会造成任何影响,小于-n+1/m时人会略微放低声音,而小于-n-1/m则会使人彻底闭口不言。

    S_n(t) 决定了其听到声音的方式,且满足 x=S_n(x,x,……,x) ,视情况可以是随距离衰减,或直接对所有 f_n 取平均。 g_n(t) 表示他在t时刻发言的意愿,g(t)均值为1,在0与2间过渡平滑的随机的跳动,此处我令g_n(t-X)=1+tanh(7*(sin(A*t)+sin(B*t)+……+sin(E*t)) ,其中A,B…E都是诸如 \sqrt{11.4514} 的无理数,而X则是对每个n都不同的随机数。

    g(t)的函数图像,此处y坐标小了一半,意会即可

    最末一项 0.001g_n(t) 是用来给 f_n 的取值兜底的,以免突变过于剧烈使得其直接严格跌落到0,那就没得玩了,突变后音量还得升回来呢。然而这一项多少有点败笔的意思,总之它“唯像”式的保证了突变以及沉默后音量的回升。

    此处我将环境定在一个四十人的教室,考虑到狭小的教室内声音几乎不会衰减,我设 S_n=\frac{f_1+f_2+……+f_{40}}{40} 。这一举动将极大的方便了我们的计算,因为我们可以直接将方程改写为 F(t)=\frac{g_1+g_2,+……+g_{40}}{40}*\frac{ln(1+\frac{10}{3}\int_{t-0.3}^{t}F(t)dt)}{ln(2)}*\frac{1}{2}(1+tanh(\color{red}{5}*[10(\int^{t}_{t-0.25}F(t)dt-\int^{t-0.5}_{t-0.75}F(t)dt)+\color{red}{1.5}]))+0.001\frac{g_1+g_2+……+g_{40}}{40}仅用到一个F,而不是40个F和f,这将在后面的数值模拟中帮助我们节省大量的算力。

    下面就到了喜闻乐见的写程序(拉屎山)环节:

    import math
    import numpy as np
    #迭代方程
    def A(x,y):
    	k=math.log1p(x)/math.log(2)*0.5*(1+math.tanh(5*(y+1.5)))
    	return k
    #随机函数
    def a(t):
    	c=1+math.tanh(7*(math.sin((math.sqrt(3))*0.7*t)+math.sin((math.sqrt(5))*0.7*t)+0.332*math.sin((math.sqrt(16))*0.7*t)+math.sin((math.sqrt(14))*0.7*t)+1.02*math.sin((math.sqrt(2.5803)*0.7*t))))
    	return c
    
    def b(t):
    	d=(a(t)+a(t+10)+a(t+20)+a(t+30)+a(t+40)+a(t+50)+a(t+60)+a(t+70)+a(t+80)+a(t+90))/10
    	return d
    
    def g(t):
    	e=(b(t)+b(t+100)+b(t+200)+b(t+300))/4
    	return e
    #时间步长
    
    step=round(0.05,5)	
    
    #F在t为0前取值的初始化
    dict={}#迭代数据暂存
    hhh=-1
    while hhh<=0:
    	dict[hhh]=g(hhh)
    	hhh=round(hhh+step,5)
    
    #记录突变时间	
    timelist=[]	
    count=0	
    realcount=0
    #用于绘图记录数据
    datadict={}#响度记录
    gdata={}#发言意愿记录
    #迭代循环	
    t=0
    while t<=360000:
        fl=(dict[round(t-0.05,5)]+dict[round(t-0.1,5)]+dict[round(t-0.15,5)]+dict[round(t-0.2,5)]+dict[round(t-0.25,5)]+dict[round(t-0.3,5)])/6
        fv=(((dict[round(t-0.05,5)]+dict[round(t-0.1,5)]+dict[round(t-0.15,5)]+dict[round(t-0.2,5)]+dict[round(t-0.25,5)])/5)-((dict[round(t-0.55,5)]+    dict[round(t-0.6,5)]+dict[round(t-0.65,5)]+dict[round(t-0.7,5)]+dict[round(t-0.75,5)])/5))*2	
        dict[t]=g(t)*A(fl,fv)+0.001*g(t)
        datadict[t]=dict[t]
        gdata[t]=g(t)
        del dict[round(t-0.8,5)]
        #以上递推模拟
        last=count
        if (dict[round(t-0.05,5)]+dict[round(t-0.1,5)]+dict[round(t-0.15,5)]+dict[round(t-0.2,5)]+dict[round(t-0.25,5)])/5>=0.1:
        	count=0
        else:
        	count=1
        	
        if last-count<=-1:
        	realcount=realcount+1
        	timelist.append(t)
    #以上突变计录  	
       
        t=round(t+step,5)
       	
    
    
    #1.75,50h,0     1.6 43    1.5 187(100h410个)     1.4 约475     1.3 约1250     1.2 约2550        1.1 约5075    1 约9200
    #以下突变绘制
    import matplotlib.pyplot as plt
    #绘图循环
    n=1
    databar={}
    kkk=-5
    tmd=0.5
    while kkk<=5:
    	databar[kkk]=0
    	kkk=round(kkk+0.05,5)#平均值初始化
    while n<=realcount:
        ctime=timelist[(n-1)]
        x_values = list(np.arange(ctime-5, ctime+5,0.05))
        y_values = [datadict[round(x,5)] for x in x_values]
       # g_values= [gdata[round(x,5)] for x in x_values]
        
        x_values=list(np.arange(-5,5,0.05))
        if datadict[round(ctime,5)]>=0.06:
        	tmd=0.7
        else:
        	tmd=0.01
        plt.plot(x_values, y_values,alpha=tmd)
        
    #    plt.plot(x_values,g_values,"c-") 
        n=round(n+1,2)
    T=-5
    while T<=5: #求均值循环
        for m in timelist:
    	    databar[T]=databar[T]+(datadict[round(m+T,5)])/realcount
        T=round(T+0.05,5)
    data_values = [databar[round(x,5)] for x in x_values]
    plt.plot(x_values, data_values,"c-")
    plt.axis([-5, 5, 0, 2])
    plt.show()

    这里的可视化部分我按需修改了很多,放上来的这一版最终的效果是筛选所有突变,绘制均值并着重标出偏离过大的数据。想要自己跑着玩的话还请自行修改

    万事俱备,终于可以开始模拟了。在模拟运行的第512s,第一个突变出现了!(ohhhhhhhhhhhh)

    F的原始数据
    F(蓝)与g平均(橙)的图像

    随手写的式子,竟然真的产生了符合预期的输出。让程序继续运行下去,在 m=5,n=1.5 的参数下,模拟的100h内产生了410个突变,这是记录的一些有趣的数据:

    以突变为中心前后五秒内声音的变化

    当然这样零散的数据意义不大,接下来我们将这410个突变绘制在一起并取平均值:

    黑色粗线是均值

    可见在本模型下,“突然安静”现象的典型特征是音量先经历一段时长在0.4s左右突增,随后以相反的速度在相同的时间内减小到0.75左右,再随后剧烈的衰减至零,保持一至两秒静默后回升。这在现实中均有对应解释,对应着“凑巧出现的波谷前对应的波峰”→“凑巧出现的波谷”→“众人察觉到什么,纷纷闭嘴”→“大家一脸懵逼”→“危机解除,继续聊天”的过程。

    值得注意的是图中几条快速回升的数据,它们在下降段同样不如其它数据剧烈,可以将其解释为当下降过程延长时,最后一个人察觉前已经有人意识到“老师没有来,这是场误会”,于是没有出现彻底的静默。

    如图,偏离均值的数据,占比0.1左右

    在我出生至现在的七坤年中,我一共经历了三次“突然安静”现象,其中包括一次“不彻底的突变”。虽然能够使得这一场景出现的条件(原则上不能说话的时间,老师不在,大家聊的热火朝天)很少能一起满足,我仍然认为100h内出现410次突变过高了。我又在不同的参数n下进行了时长50h的模拟,结果如下:

    n=1,184次/h

    n=1.1,101.5次/h

    n=1.2,51次/h

    n=1.3,25次/h

    n=1.4,9.5次/h

    n=1.5,4.1次/h

    n=1.6,0.86次/h

    n=1.75,0次

    可见每小时安静数大致满足公式y(n)=e^{-8.641412n+14.162}

    还有不少想说的,以后慢慢写罢



    来源:知乎 www.zhihu.com
    作者:BaoPinsUi啊

    【知乎日报】千万用户的选择,做朋友圈里的新鲜事分享大牛。 点击下载


沪ICP备19023445号-2号
友情链接