CSP——脉冲神经网络
时间:2022-12-15 13:00:01
题目背景
在这个问题中,你需要实现一个问题 SNN(spiking neural network,脉冲神经网络)模拟器。 SNN 由以下部分组成:
- 神经元:按一定公式更新内部状态,接受脉冲并释放脉冲
- 脉冲源:在特定时间释放脉冲
- 突触:连接神经元-神经元或脉冲源-神经元,传递脉冲
题目描述
神经元将按照一定的规则更新其内部状态。在这个问题中,我们离散时间,即设置时间间隔Δt,只考虑时间间隔整数倍的时间t=kΔt(k∈Z ),按以下公式,从k?1时刻值计算k时间变量的值:
vk=vk?1 Δt(0.04vk?12 5vk?1 140?uk?1) Ik
uk=uk?1 Δta(bvk?1?uk?1)
其中v和u神经元内部的变量会随着时间的推移而变化,a和b是常量,不会随着时间变化;其中Ik表示神经元在k如果不接受脉冲,则始终接受的所有脉冲输入的强度之和,那么Ik=0。如果满足上述计算vk≥30.神经元会发出脉冲,脉冲通过突触传播到其他神经元;同时,vk设为c并且uk设为uk d,其中c和d也是常量。图 1 展示一个神经元v曲线随时间变化。
图1: 神经元v曲线随时间变化
突触表示的是神经元-神经元、脉冲源-神经元的连接关系,包含一个入结点和一个出结点(可能出现自环和重边)。当突触的入结点(神经元或脉冲源)在k随时发出脉冲,然后延迟传播D(D>0)一刻之后,也就是k D始终突触的出结点(神经元)将接受强度w的脉冲。
脉冲源在每个时刻以一定的概率发放一个脉冲,为了模拟这个过程,每个脉冲源有一个参数0 C 版本: Python 版本: Java 版本: 每次按编号顺序从小到大,每个脉冲源调用上述伪随机函数一次,当r>myrand()脉冲在当前时间释放,并通过突触传播到神经元。 模拟时,已知 0 每个神经元的状态从时间刻开始 1 按照上述规则计算时间刻,直到完成T计算时间,然后输出T总是神经元v值和发放的脉冲次数分别的最小值和最大值。 按以下顺序规定输入数据中的结点:[0,N?1]为神经元编号,[N,N P?1]为脉冲源编号。 请在代码中使用双精度浮点类型。 读入数据从标准输入。 输入的第一行包括四个空间分隔的正整数NSPT,表示一共有N个神经元,S个突触和P脉冲源,输出时间T时神经元的v值。 输入的第二行是正实数Δt,表示时间间隔。 输入接下来的几行,每行都有空间分隔的正整数RN和六个实数vuabcd,每行按顺序对应RN具有相同初始状态和常量的神经元:vu表示神经元在时刻 0 时间的变量值;abcd神经元微分方程中的四个常量。保证所有的RN加起来等于N。保证所有的RN加起来等于N。它们从前到后按编号顺序描述神经元,每行对应连续编号的神经元信息。 下一步输入P行,每行是正整数r,每行按顺序对应一个脉冲源r参数。 下一步输入S每行有两个空格分隔的整数s(0≤s 输出到标准输出。 输出有两行,第一行由两行保留 3 位小数的实数组成是所有神经元在任何时候T时变量v最小值和最大值。第二行由两个整数组成,即所有神经元在整个模拟过程中释放脉冲次数的最小值和最大值。 只要按照题目要求正确实现,就不会因为计算精度问题而得到错误的答案。 该样例有 1 个神经元、1 个突触和 1 脉冲源,时间间隔Δt=0.1.脉冲强度是唯一的脉冲源 30.传播延迟为0 2 突触传播到唯一的神经元。 这个例子一起进行 10 随机数生成器生成时间步模拟 10 次随机数如下: 因此,唯一的脉冲源始终存在 1-4 和 6-10 发出脉冲。 1 到 10 时间,唯一的神经元v值分别为: 神经元在时刻 5 和时刻 9 发放,最后得到v=?35.608。 看完题目,了解题目的意思,就知道是模拟题。根据题目的意思,一步一步模拟现实就好了,当然要注意一下代码的时间和空间复杂度。先放上AC代码稳定军心。 AC代码: 下面就来讲解一下代码以及几点疑惑: 1、为什么v,u,a,b,c,d不直接弄个神经元结构体,而是分别用数组存储?在空间上,结构体存储和各个参数分开存储没有区别,但是在获取数据的方式上略微不同,将导致耗费的时间不同。例如,要获取神经元结构体中的参数v,计算机要先在神经元结构体数组中找到该神经元,然后再在该神经元结构体这块内存中再次寻找参数v(类比间接寻址),而用数组存储不同,计算机直接在数组v中顺序查找,直接获取对应参数v(类比直接寻址)。因为本题卡时长,所以每一步优化都很关键。 2、设立vector 3、ik二维数组为什么是行为时间点,列为目标神经元,而不是行为目标神经元,列为时间点?在模拟的时候,最外围for循环是时间,然后依次遍历所有的神经元在该时刻所受的脉冲之和,即ik。我们知道二维数组的写入和读取都是一行一行的执行,先执行完一行再执行下一行,当ik中行为目标神经元,列为时间点,在每一时刻,每次访问神经元对应ik都要一行一行的执行,就会造成时间上的浪费。而行为时间点,列为目标神经元时,当访问该时刻时,一行都是接下来要访问的神经元,所以缩短了访问时间。 4、为什么时间要取模1000?因为直接数组e二维数组开10^5*10^3, 空间复杂度度:(4+8+4)*10^5*10^3=1600MB,超内存了,所以不能开10^5。取模1000,因为D最大是1000,当前面时刻的ik用完之后,可以清零直接覆盖掉,不会产生影响。 其它需要注意的点,注释上也有说明,如果还有不清楚的可以留言哦! static unsigned long next = 1; /* RAND_MAX assumed to be 32767 */ int myrand(void) { next = next * 1103515245 12345; return((unsigned)(next/65536) % 32768); }
next = 1 def myrand(): global next next = (next * 1103515245 12345) % (2 ** 64) return (next // 65536) % 32768
long next = 1; int myrand() { next = next * 1103515245 12345; return (int)((Long.divideUnsigned(next, 65536)) % 32768); }
输入格式
输出格式
样例1输入
1 1 1 10 0.1 1 -70.0 -14.0 0.02 0.2 -65.0 2.0 30000 1 0 30.0 2
样例1输出
-35.608 -35.608 2 2
样例1解释
16838 5758 10113 17515 31051 5627 23010 7419 16212 4086
-70.000 -70.000 -40.000 -8.200 -65.000 -35.404 -32.895 0.181 -65.000 -35.608
样例2输入
2 4 2 10 0.1 1 -70.0 -14.0 0.02 0.2 -65.0 2.0 1 -69.0 -13.0 0.04 0.1 -60.0 1.0 30000 20000 2 0 15.0 1 3 1 20.0 1 1 0 10.0 2 0 1 40.0 3
样例2输出
-60.000 -22.092 1 2
子任务
子任务
T
N
S
P
D
分值
1
≤102
≤102
≤102
≤102
≤102
30
2
≤103
≤103
≤103
≤103
≤103
40
3
≤105
≤103
≤103
≤103
≤10
30
#include