面向对象第三单元总结

PART 1 : 自测数据策略

本单元最大的特点就是中弱测极其友好,甚至能做到一遍过,但是后来能发现很多指令其实中弱测没测(),就很坑,不得不自己想办法去测试,由于不会写评测机和对拍,所以就只是自己构造几个小一点的(10个点)左右的尽量稠密的图,结果自己嗯造的数据还是没找到什么bug,路径这些最后没啥问题,反而是钱这种简单的属性除了问题,让我深感自己造数据的无力

PART 2 : 架构设计分析

  • 第九次作业

 

 

本次作业中为了避免二重循环卡tle,我采用动态维护的策略,对一个组加人删人时会改变年龄平均值以及组内的valueSum(此外对network进行addRelation操作也会变动),对一个Network进行增删人以及加关系等操作时会变动blockingsum,方便直接查询

另外本次作业引入了并查集结构,我采用了从id到其根id的hashmap映射存储,该并查集支持秩优化,节省了一定的查询时间,没有被tle卡

  • 第十次作业

 

 

本次作业引入了新机制message,实现几个人的通行,同时引入社交值等概念,本次实际上仍然只是使用并查集结构,并没有太大难点

  • 第十一次作业

 

 

本次作业引入了最短路径算法,我使用了dijkstra算法来完成,因为dijkstra的贪心,我做了当找到目标点的最短路径及时break的优化,但是因为使用了hashmap所以可能优化效果并不确定,其余并没有什么值得分享的地方.

PART 3 : 分析bug

第九次作业:本次作业错误了两个点,发现一个问题,是关于并查集构造的,对比标准输出我发现,我把并查集对应的hashmap作为了MyPerson类的静态属性,在构造MyPerson的时候,会改动这一个类,结果就会导致,如果重名的人进来,会报错,但是在那之前他已经调用了构造方法,使得原来相同id的person失去了他的并查集数据,引发了bug

第十次作业:本次作业错误有两处,一处与整除运算相关,一处与并查集相关;第一处是,我算钱和维护平均数,都是整除后再乘以size,这中间会折损余数,而我完全没意识到,第二处则是我使用,并查集结构处理求最小生成树的时候,cv上次代码,结果忘了改某个函数

第十一次作业:本次中弱测相对较强,发现自己的bug在dijkstra方法中由于某些情况不计入最短路径导致了错误.解决后就没有什么大的问题了

PART 4 : 趣味拓展

这次趣味拓展中,笔者设想实现简单供销模拟,条件如下

1.一个广告商只代理一个生厂商的产品,而一个生产商也正好只生产一类商品,那么查询商品销售路径等可以抽象为查询生产商到消费者的路径

2.不考虑生产过程的现实性,所以我们假装生产者的商品数无限

3.为了避免繁复地引入现实中生产者先销售给代理商,代理商多级销售给消费者中存在的部分代理商货量不足需要协调货物,多级代理等问题,我们规定,在广告商只负责广告和拿提成,购买时货物直接由厂商到消费者.

在Network引入了三种角色,均为Person的子类,分别为消费者,生产者,以及广告商,考虑到三者周转的同时有商品以及钱,故为消费者引进当前商品数(commodity)属性,为广告商增加供货商id(proid)属性,为生产者提供销售量(sold)属性;

引入了一个记录销售路径的paths

@ public instance model non_null Customer[] customers;//plus int commodity
@ public instance model non_null Advertiser[] advertisers;//plus int proid
@ public instance model non_null Producer[] producers;//plus int sold
@ public instance model non_null int[][3] paths;//start pro trans adv dst cust

因为这三者均为person子类,所以笔者设计的生成函数,意图将新加的人同时加入到该去的群组(如producer)以及people这个大的容器里面,注意,这里只有各类人的名单,不用去因为其内部属性变化而去更改这三个容器(如广告商增加了供货商id,不用变动advertisers)

 public void addCustomer(/*@ non_null @*/Customer customer) throws EqualPersonIdException;
public void addAdvertiser(/*@ non_null @*/Advertiser advertiser) throws EqualPersonIdException;
public void addProducer(/*@ non_null @*/Producer producer) throws EqualPersonIdException;

相应的,我们模仿network接口中原有的contains方法,构造出相应的查询函数,

 public /*@ pure @*/ boolean custcontains(int id);
public /*@ pure @*/ boolean advcontains(int id);
public /*@ pure @*/ boolean procontains(int id);

同时,我们应该设计好未找到的异常

public abstract class CustIdNotFoundException extends Exception
public abstract class AdvIdNotFoundException extends Exception
public abstract class ProIdNotFoundException extends Exception

考虑到原本PersonIdNotFoundException的存在,先判别是否contains,再判断以上异常较为合适

接下来要做的就是对三者业务的模拟,业务仍旧基于原有的message开展.同时我们需要分析生产商(producer)推广,广告商(advertiser)经销以及消费者(customer)购买,查询某厂商销售额以及销售路径的方法.其中生产商(producer)推广,广告商(advertiser)经销及查询某厂商销售额书写jml

  • 生产商推广:这一个方法想要顺利执行就要有合适的生产商与广告商,前提应该是两者存在间接联系,能够达成合作,抽象为图,应该只是查询两者是否连通,若连通且不直接相连则借助原addRelation方法加边即可(也有可能抛出PersonIdNotFoundException)异常,但不考虑EqualRelationException,原因是在原有关系基础上建立了"推广"这一抽象关系

    public void produceDo(int id1, int id2, int value) throws ProIdNotFoundException,AdvIdNotFoundException,PersonIdNotFoundException;
     @ public normal_behavior
    @ requires procontains(id1) && advcontains(id2) && isCircle(id1,id2) && !getPerson(id1).isLinked(getPerson(id2));
    @ assignable people //以下缩进行来源于addrelation的jml描述
        @ ensures people.length == \old(people.length)
        @ ensures (\forall int i; 0 <= i && i < \old(people.length);
        @         (\exists int j; 0 <= j && j < people.length; people[j] == \old(people[i])));
        @ ensures (\forall int i; 0 <= i && i < people.length && \old(people[i].getId()) != id1 &&
        @     \old(people[i].getId()) != id2; \not_assigned(people[i]));
        @ ensures getPerson(id1).isLinked(getPerson(id2)) && getPerson(id2).isLinked(getPerson(id1));
        @ ensures getPerson(id1).queryValue(getPerson(id2)) == value;
        @ ensures getPerson(id2).queryValue(getPerson(id1)) == value;
        @ ensures (\forall int i; 0 <= i && i < \old(getPerson(id1).acquaintance.length);
        @         \old(getPerson(id1).acquaintance[i]) == getPerson(id1).acquaintance[i] &&
        @         \old(getPerson(id1).value[i]) == getPerson(id1).value[i]);
        @ ensures (\forall int i; 0 <= i && i < \old(getPerson(id2).acquaintance.length);
        @         \old(getPerson(id2).acquaintance[i]) == getPerson(id2).acquaintance[i] &&
        @         \old(getPerson(id2).value[i]) == getPerson(id2).value[i]);
        @ ensures getPerson(id1).value.length == getPerson(id1).acquaintance.length;
        @ ensures getPerson(id2).value.length == getPerson(id2).acquaintance.length;
        @ ensures \old(getPerson(id1).value.length) == getPerson(id1).acquaintance.length - 1;
        @ ensures \old(getPerson(id2).value.length) == getPerson(id2).acquaintance.length - 1;
    @ ensures ((Advertiser)getPerson(id2)).getProid == id1;
    @ also
    @ public normal_behavior
    @ requires procontains(id1) && advcontains(id2) && getPerson(id1).isLinked(getPerson(id2));  
    @ assignable people
    @ ensures ((Advertiser)getPerson(id2)).getProid == id1;
    @ also
    @ public exceptional_behavior
    @ assignable \nothing;
    @ requires !contains(id1) || !contains(id2);
    @ signals (PersonIdNotFoundException e) !contains(id1);
    @ signals (PersonIdNotFoundException e) contains(id1) && !contains(id2);
    @ also
    @ public exceptional_behavior
    @ assignable \nothing;
    @ requires !procontains(id1);
    @ signals_only ProIdNotFoundException
    @ also
    @ public exceptional_behavior
    @ assignable \nothing;
    @ requires !advcontains(id2);
    @ signals_only AdvIdNotFoundException

    public void produceDo(int id1, int id2, int value) throws ProIdNotFoundException,AdvIdNotFoundException,PersonIdNotFoundException;

     

  • 广告商经销:这个方法其实类似微商发pyq,打扰pyq开放群组的每一个人,所以行为类似群发消息,同时广告商发广告会拿到广告费,由供货商出,id为messageid

    public void AdvertiseDo(int id1, int id2, int id,int salary) throws ProIdNotFoundException,AdvIdNotFoundException,PersonIdNotFoundException, MessageIdNotFoundException;

    jml如下

    @ public normal_behavior
    @ requires containsMessage(id) && getMessage(id).getType() == 1 && getMessage(id).getPerson1()==getPerson(id2)&&getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1());//以下若缩进则同sendmessage,不同点在于保证发消息人加了salary且厂商不止拿了红包,还亏钱.
          @ assignable people[*].socialValue, people[*].money, messages, emojiHeatList;
          @ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
          @         (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
          @         (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
          @ ensures (\forall Person p; \old(getMessage(id)).getGroup().hasPerson(p); p.getSocialValue() ==
          @         \old(p.getSocialValue()) + \old(getMessage(id)).getSocialValue());
          @ ensures (\forall int i; 0 <=i&&i<people.length&&!\old(getMessage(id)).getGroup().hasPerson(people[i]);
          @         \old(people[i].getSocialValue()) == people[i].getSocialValue());
          @ ensures (\old(getMessage(id)) instanceof RedEnvelopeMessage) ==>
          @         (\exists int i; i==((RedEnvelopeMessage)\old(getMessage(id))).getMoney()
          @/\old(getMessage(id)).getGroup().getSize();
    @           \old(getMessage(id)).getPerson1().getMoney() ==
    @           \old(getMessage(id).getPerson1().getMoney()) - i*(\old(getMessage(id)).getGroup().getSize() - 1) + salary &&
    @           (\forall Person p; \old(getMessage(id)).getGroup().hasPerson(p) && p != \old(getMessage(id)).getPerson1() && p != getPerson(id1);
    @           p.getMoney() == \old(p.getMoney()) + i));
    @           (\forall Person p; \old(getMessage(id)).getGroup().hasPerson(p) && p != \old(getMessage(id)).getPerson1() && p == getPerson(id1);
    @           p.getMoney() == \old(p.getMoney()) + i)-salary);
    @ (\forall Person p; \old(getMessage(id)).getGroup().hasPerson(p) ; p != getPerson(id1))==>
    ((getPerson(id1).getMoney==\old(getPerson(id1).getMoney)-salary)
          @ ensures (\old(getMessage(id)) instanceof RedEnvelopeMessage) ==>
          @         (\forall int i; 0 <= i && i < people.length && !\old(getMessage(id)).getGroup().hasPerson(people[i]);
          @           \old(people[i].getMoney()) == people[i].getMoney());
          @ ensures (\old(getMessage(id)) instanceof EmojiMessage) ==>
          @         (\exists int i; 0 <= i && i < emojiIdList.length && emojiIdList[i] == ((EmojiMessage)\old(getMessage(id))).getEmojiId();
          @         emojiHeatList[i] == \old(emojiHeatList[i]) + 1);
          @ ensures (!(\old(getMessage(id)) instanceof EmojiMessage)) ==> \not_assigned(emojiHeatList);
    @ ensures (!(\old(getMessage(id)) instanceof RedEnvelopeMessage)) ==> ((getPerson(id1).getMoney==\old(getPerson(id1).getMoney)-salary)&&(getPerson(id2).getMoney==\old(getPerson(id2).getMoney)+salary));
    @ also
    @ public normal_behavior
    @ requires containsMessage(id) && (getMessage(id).getType() == 0 || getMessage(id).getPerson1()!=getPerson(id2))
    @ assignable \nothing;
    @ also
    @ public exceptional_behavior
    @ signals (MessageIdNotFoundException e) !containsMessage(id);
    @ also
    @ public exceptional_behavior
    @ assignable \nothing;
    @ requires !contains(id1) || !contains(id2);
    @ signals (PersonIdNotFoundException e) !contains(id1);
    @ signals (PersonIdNotFoundException e) contains(id1) && !contains(id2);
    @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 1 &&
    @         !(getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1()));
    @ also
    @ public exceptional_behavior
    @ assignable \nothing;
    @ requires !procontains(id1);
    @ signals_only ProIdNotFoundException
    @ also
    @ public exceptional_behavior
    @ assignable \nothing;
    @ requires !advcontains(id2);
    @ signals_only AdvIdNotFoundException

    public void AdvertiseDo(int id1, int id2, int id,int salary) throws ProIdNotFoundException,AdvIdNotFoundException,PersonIdNotFoundException, MessageIdNotFoundException;
  • 消费者购买:这个方法本质类似sendIndirectMessage,除了本身的打钱.还为消费者增加了商品数commodity,为厂商增加了销售额sold,为network增加个path

      public int CustomerDo(int proid,int advid,int custid,int id) throws        ProIdNotFoundException,AdvIdNotFoundException,CustIdNotFoundException,PersonIdNotFoundException,MessageIdNotFoundException,
  • 查询厂商销售额

    public /*@ pure @*/ int querySold(int proid) throws        ProIdNotFoundException,PersonIdNotFoundException
     @ public normal_behavior
    @ requires procontains(pro)
    @ ensures \result == ((Producer)getPerson(proid)).getSold;
    @ also
    @ public exceptional_behavior
    @ signals (PersonIdNotFoundException e) !contains(proid);
    @ signals (ProIdNotFoundException e) contains(proid) && !procontains(pid);

    public /*@ pure @*/ int querySold(int proid) throws       ProIdNotFoundException,PersonIdNotFoundException
  • 查询销售路径:遍历path那个二维数组,找到0处为proid,2处为custid的路径

    public  /*@ pure @*/int[] querypath(int proid,int custid) throws        ProIdNotFoundException,PersonIdNotFoundException,CustIdNotFoundException

以上便为我的设想

PART 5 : 学习体会

本单元很幸运的较为平稳地度过了,两个重要想法是,第一真得学着写评测机造数据了,第二个想法是要学着尽量优化自己的代码,比如第九次作业就开始出现的qbs等指令,我通过动态维护避免了二重循环,就没出现TLE等问题,其实很多事情稍微动动脑就能想到简单的优化方案,只要努力投入,我相信我一定会在oo课上收获许多知识.

posted on 2022-06-04 21:30  酷clear  阅读(56)  评论(1编辑  收藏  举报