OO 第三单元总结

OO 第三单元总结

规格的阅读与实现心得

JML的阅读方法

  1. 语法上, 可以参考课程组的《JML level0手册》, 涵盖了基本的jml关键词和语法,看不明白的话可以多翻翻,类比着就搞懂了
  2. 阅读顺序上, 阅读JML可以从一些比较底层的类开始读, 比如 Person, Message这种依赖关系比较少的类, 可以先读起来,这样对一个类的功能就可以很快的了解
  3. 阅读tips : Idea对于JML高亮支持其实很不好(基本没有啊喂)推荐在vscode上装好插件在上面阅读,

OO 第三单元总结

还可以对Idea注释调颜色,这样至少看起来清楚一点不会灰糊糊的一团

JML迭代

每次迭代推荐用Idea 鼠标右键的compare with clipboard, 比较一下差异, 同时注意一下一些坑人限制条件比如1111

测试数据与JML

本单元一开始想试试单元测试的,但咨询了一下学长们,似乎都不太推荐JUNIT, OPENJML等基于JML的测试, 后来经过尝试确实有点麻烦。OpenJml 就是个jml语法检查器(bushi, Junit有点像Verilog的testbench样例还需要自己构造,如果能够有一个可以根据JML直接校验函数的工具就好了

剩下的日子里, 就都在写对拍机了,在构造测试数据时, JML的存在也可以使测试数据的覆盖率提高, 就比如am指令的构造

/*@ public normal_behavior
      @ requires !(\exists int i; 0  containsEmojiId(((EmojiMessage) message).getEmojiId()) &&
      @            (message.getType() == 0) ==> (message.getPerson1() != message.getPerson2());
      @ assignable messages;
      @ ensures messages.length == \old(messages.length) + 1;
      @ ensures (\forall int i; 0
      @                                     containsEmojiId(((EmojiMessage) message).getEmojiId())) &&
      @                                     message.getType() == 0 && message.getPerson1() == message.getPerson2();
      @*/

## 依据JML构造数据来覆盖所有可能分支 ##

def am():
    ty = random.randint(0, 1)
    sv = random.choice(extreme_sv_pool)
    if random.randint(0, excRate) == 1:  # emi
        try:
            id = random.choice(list(message_pool.keys()))
        except:
            id = random.randint(idmin, idmax)
        if ty == 0:
            try:  # {id:{type:, pid1:, pid2:, sv:, gid:,}}
                if random.randint(0, excRate) == 1:
                    # 在sm时会触发异常:
                    # epi & pinf情况, 异常可控
                    if random.randint(0, 2) == 1:
                        pid1 = pid2 = random.choice(list(person_pool.keys()))  # epi
                    else:
                        pid1 = pid2 = random.randint(idmin, idmax)  # pinf when send
                elif random.randint(0, 2 * excRate) == 1:
                    pid1 = random.randint(idmin, idmax)
                    pid2 = random.randint(idmin, idmax)

                else:
                    pid1, pid2 = random.sample(list(person_pool.keys()), 2)
                    # ADD! 真正加入, 用一点数据结构记录
                    message_pool[id] = {"type": ty, "sv": sv, "pid1": pid1, "pid2": pid2, "gid": None, "div": "am"}
            except:
                pid1 = random.randint(idmin, idmax)
                pid2 = random.randint(idmin, idmax)

            return "am {} {} {} {} {}".format(id, sv, ty, pid1, pid2)
        else:
            # pinf ginf 情况 覆盖了所有异常,进行测试
            if random.randint(0, excRate) == 1:
                pid1 = random.randint(idmin, idmax)
                gid = random.randint(idmin, idmax)
            else:
                pid1 = random.choice(list(person_pool.keys()))
                try:
                    gid = random.choice(list(group_pool.keys()))
                except:
                    return ag()
                # ADD! 真正的加入
                message_pool[id] = {"type": ty, "sv": sv, "pid1": pid1, "pid2": None, "gid": gid, "div": "am"}
            return "am {} {} {} {} {}".format(id, sv, ty, pid1, gid)

    else:
        id = random.randint(idmin, idmax)
        if ty == 0:
            try:
                if random.randint(0, excRate) == 1:  # epi
                    if random.randint(0, 2) == 1:
                        pid1 = pid2 = random.choice(list(person_pool.keys()))
                    else:
                        pid1 = pid2 = random.randint(idmin, idmax)
                elif random.randint(0, 2 * excRate) == 1:
                    pid1 = random.randint(idmin, idmax)
                    pid2 = random.randint(idmin, idmax)
                else:
                    pid1, pid2 = random.sample(list(person_pool.keys()), 2)
            except:
                pid1 = random.randint(idmin, idmax)
                pid2 = random.randint(idmin, idmax)
            message_pool[id] = {"type": ty, "sv": sv, "pid1": pid1, "pid2": pid2, "gid": None, "div": "am"}
            return "am {} {} {} {} {}".format(id, sv, ty, pid1, pid2)
        else:
            pid1 = random.choice(list(person_pool.keys()))
            try:
                gid = random.choice(list(group_pool.keys()))
                message_pool[id] = {"type": ty, "sv": sv, "pid1": pid1, "pid2": None, "gid": gid, "div": "am"}
                return "am {} {} {} {} {}".format(id, sv, ty, pid1, gid)
            except:
                return ag()

容器的选择

一些容器和我的选择, 因为一些容器需要O(1)的查询效率, 那么 Hashmap是最好的选择

第三次作业中, Message的存储满足的是一个FIFO的规则, 所以用了 linkedlist

Exception

    private int id; //personId触发了异常
    private static int totalExcCnt = 0;
    private static HashMap excTable = new HashMap<>();
    // 触发的personId - 触发的次数

Group

    private HashMap people = new HashMap<>();
    // id - person

Person

    private HashMap linkage = new HashMap<>();
    // 邻居Person - distance
    private LinkedList messages = new LinkedList<>();
    // Person收到的信息

Network

    private HashMap people = new HashMap<>();
    private HashMap groups = new HashMap<>();
    private HashMap messages = new HashMap<>();
    private HashMap emojiHeatMap = new HashMap<>();
    // 上述皆为 的Hashmap, O(1)查找不错的
    private HashSet edges = new HashSet<>();
    // Edge是我自己定义的边类, edges记录了所有的边, kruskal的时候会比较方便
    private MyDsu dsu = new MyDsu<>();
    // Dsu是我自己写的优化完全的并查集类, 支持不同模板很好用

算法和性能分析

本单元有一些需要注意算法时间复杂度的点

  1. 维护 valueSumageSumageSquareSum 如果要求 ageVar那么为了保证精度与jml一致, 自己推一下就好
return (timeSum - 2 * ageSum * getAgeMean() +  getAgeMean() * getAgeMean() * people.size()) / people.size();
////////////////////////////////////////////////////////////////////

BigInteger part1 = ageSquareSum;

BigInteger part2 = (new BigInteger("2")).multiply(ageSum)
    .multiply(BigInteger.valueOf(getAgeMean()));

BigInteger part3 = BigInteger.valueOf(getAgeMean()).multiply(BigInteger.valueOf(getAgeMean()))
                                .multiply(BigInteger.valueOf(people.size()));

BigInteger result = (part1.subtract(part2).add(part3)).divide(BigInteger.valueOf(people.size()));

return result.intValue();

需要注意的是: valueSum是将每对关系计算两次,需要在每次加人时 valueSum2 * value。除了在加人删人外,在添加关系时需要遍历所有 group维护 valueSum。(别忘了哦!)
2. Kruskal算法, 需要提前维护所有的边集, 然后遍历边集找到所有在联通分量的边。这样可以实现O(ElogE)的复杂度, 如果不存储需要用iscircle再查找可能会慢
3. isCircle方法, 要用路径压缩+合并秩优化的并查集, 鉴于网上似乎还没有java版的,我给一个吧嘿嘿

import java.util.HashMap;

public class MyDsu {
    private HashMap fatherMap = new HashMap<>();
    private HashMap rankMap = new HashMap<>();

    public MyDsu() {
    }

    public void addElement(T a) {
        fatherMap.put(a, a);
        rankMap.put(a, 1);
    }

    public T findRoot(T a) {
        T r = a;
        while (fatherMap.get(r) != r) {
            r = fatherMap.get(r);
        }
        T i = a;
        T tmp;
        while (i != r) {
            tmp = fatherMap.get(i);
            fatherMap.put(i, r);
            i = tmp;
        }
        return r;
    }

    public void unionSet(T a, T b) {
        if (a == null || b == null) {
            return;
        }
        T root1 = findRoot(a);
        T root2 = findRoot(b);
        if (rankMap.get(root1) == rankMap.get(root2)) {
            fatherMap.put(root2, root1); //root 1 is fa
            rankMap.put(root1, rankMap.get(root1) + 1);
        } else {
            if (rankMap.get(root1) < rankMap.get(root2)) {
                fatherMap.put(root1, root2);
            } else {
                fatherMap.put(root2, root1);
            }
        }
    }

}

  1. DeleteColdEmoji 如果是按着jml的逻辑, 先遍历 heatmap找到对应待删除的 emojiMessage, 那么针对每一个emoji,都要遍历一遍整个 messages显然复杂度较大。正常想法就是先遍历 messages再遍历 heatmap, 那么就可以O(n)解决问题
  2. dijkstra算法 注意可以使用堆优化, 并使用一个内部类 node来记录相关节点信息
public class Dijkstra {

    public class Node implements Comparable {

        private int dis = 0;
        private int id = 0;

        public int getDis() {
            return dis;
        }

        public int getId() {
            return id;
        }

        public Node(int id, int dis) {
            this.dis = dis;
            this.id = id;
        }

        @Override
        public int compareTo(Node o) {
            return Integer.compare(this.getDis(), o.getDis());
        }
    }

    //
    private HashMap dis = new HashMap<>();
    //
    private HashMap done = new HashMap<>();
    //
    private HashMap people;

    private PriorityQueue queue = new PriorityQueue<>();

    public Dijkstra(HashMap people) {
        this.people = people;
        for (Map.Entry personEntry : people.entrySet()) {
            int id = personEntry.getKey();
            dis.put(id, (int) 1e9);
            done.put(id, false);
        }
    }

    public int run(int fromId, int toId) {
        //算法本体
    }
}

Network 扩展考虑

题目要求

假设出现了几种不同的Person

  • Advertiser:持续向外发送产品广告
  • Producer:产品生产商,通过Advertiser来销售产品
  • Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 — 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
  • Person:吃瓜群众,不发广告,不买东西,不卖东西

如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)

首先我设计:

  1. 在Message下新增加 Advertisement类, 其中包含了 attraction 代表广告吸引力,其余属性类似于message分群发、私发等
  2. 新增加Product类用于买卖,宣传, 内置属性 storage, salesAmount
  3. productList[]保存着当前的product
  4. 每个person(作为吃瓜群众,也会收到广告) 其中可以通过getInterest(Product p) 来获取对p的a兴趣
/*@ public normal_behavior
      @ requires containsMessage(id) && getMessage(id).getType() == 0 &&
      @          getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()) &&
      @          getMessage(id).getPerson1() != getMessage(id).getPerson2();

      @ assignable messages, emojiHeatList; +
      @ assignable getMessage(id).getPerson1().socialValue, getMessage(id).getPerson1().money;
      @ assignable getMessage(id).getPerson2().messages, getMessage(id).getPerson2().socialValue, getMessage(id).getPerson2().money,
      getMessage(id).getPerson2().interests;

      @ ensures (\old(getMessage(id)) instance of Advertisement) ==>
      @         (\old(getMessage(id)).getPerson1().getInterest(Advertisement.getProduct()) ==
      @         \old(getMessage(id).getPerson1().getInterest(Advertisement.getProduct())) + ((Advertisement)\old(getMessage(id))).getAttraction()

      @ ensures (!(\old(getMessage(id)) instanceof Advertisement)) ==> (\not_assigned(people[*].getInterest(Product[*]));

        ......略
      @
      @ also
      @ public normal_behavior
      @ requires containsMessage(id) && getMessage(id).getType() == 1 &&
      @           getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1());

      @ assignable people[*].socialValue, people[*].money, people[*].interests
        messages, emojiHeatList;

        ......略

      @ ensures (\old(getMessage(id)) instance of Advertisement) ==>
      @          (\exists int i; i == ((Advertisement)\old(getMessage(id))).getAttraction();
      @           (\forall Person p; \old(getMessage(id)).getGroup().hasPerson(p) && p != \old(getMessage(id)).getPerson1();
      @           p.getInterest(Advertisement.getProduct()) == \old(p.getInterest(Advertisement.getProduct())) + i));

      @ ensures (!(\old(getMessage(id)) instanceof Advertisement)) ==> (\not_assigned(people[*].getInterest(Product[*]));

        ......略

      @ also
      @ public exceptional_behavior

      @ signals (MessageIdNotFoundException e) !containsMessage(id);
      @ signals (RelationNotFoundException e) containsMessage(id) && getMessage(id).getType() == 0 &&
      @          !(getMessage(id).getPerson1().isLinked(getMessage(id).getPerson2()));
      @ signals (PersonIdNotFoundException e) containsMessage(id) && getMessage(id).getType() == 1 &&
      @          !(getMessage(id).getGroup().hasPerson(getMessage(id).getPerson1()));
      @*/
    public void sendMessage(int id) throws
            RelationNotFoundException, MessageIdNotFoundException, PersonIdNotFoundException;

    // Customer

    /*@ public normal_behavior
      @ requires contains(producerId);
      @ requires !(\exists int i; 0

架构分析

本单元作业架构已经由课程组给出,无需多分析,重要的是阅读jml和采取合适的数据结构和算法来实现。

OO 第三单元总结

体会感想

整个单元其实期待还是很高的。但是整个单元学下来, 似乎所有的时间都用在读jml上了, 图论算法也不算难,感谢助教的怜悯,其实希望有个jml直接对接检查代码的说,结果似乎没有,感觉jml的威力瞬间减少了不少。 三次作业难得全满挺不错的,希望下次继续努力!

Original: https://www.cnblogs.com/cywuuuu/p/16303312.html
Author: cywuuuu
Title: OO 第三单元总结

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714001/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 面试手撕并发算法题

    固定打印顺序 使用 wait-notify 实现以下功能:先打印 b,再打印 a 线程t1和t2同时运行,t1中打印 a,t2中打印 b,但 t1 打印得有个前提,就是 t1要在t…

    技术杂谈 2023年6月21日
    073
  • 在 Queue 中 poll()和 remove()有什么区别?

    remove() ,如果队列为空的时候,则会抛出异常 而poll()只会返回null posted @2019-09-04 14:35 割肉机 阅读(3342 ) 评论() 编辑 …

    技术杂谈 2023年5月31日
    0109
  • [原则]产品规划怎么做?

    [原则]产品规划怎么做? [原则]产品规划怎么做? posted @2021-11-02 10:05 卖烧烤的鱼 阅读(37 ) 评论() 编辑 Original: https:/…

    技术杂谈 2023年5月30日
    0108
  • Flink on Yarn和k8s

    Yarn 架构 下图为作业提交到yarn的交互流程: 组件列表 ResourceManager (RM):ResourceManager (RM) 负责处理客户端请求、启动 / 监…

    技术杂谈 2023年7月10日
    0100
  • Python去水印方法(无需安装任何库)

    分享一个Python 自带库去水印的方法 今天用WPS将PDF转图片,发现没有会员就会自带水印,于是萌生了用Python去水印的想法 from itertools import p…

    技术杂谈 2023年7月24日
    090
  • Hbase中(java.io.IOException:Couldnotlocateexecutablenullbinwinutils.exeintheHadoopbinarie)

    报错信息如下: 结合大神分析,应该为本机使用Hbase时,没有配置其环境变量。 出处:https://www.cnblogs.com/jessezeng/p/5520915.htm…

    技术杂谈 2023年7月24日
    098
  • python 类与对象

    如果需创建一个类,那么可以使用 class 关键字:实例使用名为 a 的属性,创建一个名为 MyClass 的类: class MyClass: a = 6 现在我们可以使用名为 …

    技术杂谈 2023年6月21日
    096
  • Map接口总结(如何使用默认方法)

    default方法 排序在前的方法较为常用。 默认方法是为了在标准库中添加默认支持函数式方法,同时也不必修改现有的类,这样做虽然有一定的好处,但是实际上在子类中使用default方…

    技术杂谈 2023年7月24日
    080
  • 最左前缀有手就会,那索引下推呢?

    联合索引的最左前缀原则属于面试高频题,想必大部分同学都知道一些,但是,那些不符合最左前缀的部分,会怎么样呢(索引下推) 索引下推不算高频题,知道的同学应该不是很多(不过并不代表有啥…

    技术杂谈 2023年7月25日
    0241
  • mysql(DQL)

    MYSQL(康老师-DQL ) 1:基本的SELECT语句 1.1:基本的SELECT语句的课后练习 2:运算符 2.1:运算符课后练习 3.1排序 3.2分页 4.多表查询 4….

    技术杂谈 2023年7月25日
    057
  • SkiaSharp常用操作

    SkiaSharp常用操作 屏幕的坐标系原点在屏幕的左上角,水平往右、竖直往下为正。屏幕的坐标横坐标用”x”表示,纵坐标用”y”表示…

    技术杂谈 2023年5月31日
    0114
  • openpyxl 模块学习记录

    多单元格操作 通过切片 >>> cell_range = ws[‘A1′:’C2’] 通过行(列) >>> colC = ws[‘C’] &gt…

    技术杂谈 2023年6月21日
    067
  • 锁释放和事务提交的顺序问题

    面对高并发是锁的实现要使用aop 实现,锁不能加在方法中,应为事务一般是方法结束后提交,而锁在finally 方法中提交,从而会出现锁已经解锁而事务还没来得及提交,下个锁获得到的数…

    技术杂谈 2023年6月1日
    091
  • Apache Thrift系列(一):Thrift基本及IDL语法

    thrift支持数据类型 基本类型: bool: 布尔值 byte: 8位有符号整数 i16: 16位有符号整数 i32: 32位有符号整数 i64: 64位有符号整数 doubl…

    技术杂谈 2023年5月31日
    088
  • ruoyi接口权限校验

    此文章属于ruoyi项目实战系列 ruoyi系统在前端主要通过权限字符包含与否来动态显示目录和按钮。为了防止通过http请求绕过权限限制,后端接口也需要进行相关权限设计。 @Pre…

    技术杂谈 2023年6月21日
    098
  • 记录一次NoSuchMethodError问题的解决

    一、问题描述 今天在执行单元测试时遇到了一个NoSuchMethodError错误,完整的报错信息如下: … Caused by: javax.validation.Valid…

    技术杂谈 2023年7月11日
    082
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球