Java Ring Buffer

Ring Buffer 是一个固定容量大小的数组。维护两个下标,一个写入,一个读取。
类似一个环,怎么计算写入和读取的下标,有2种方式实现:
1.记录当前的已经写入的数量
2.记录一个反转标记

//记录写入数量,实现RingBuffer
public class RingBufferFillCount {

    public Object[] elements = null;
    private int capacity = 0;
    private int writePos = 0;
    private int available = 0;

    public RingBufferFillCount(int capacity) {
        this.capacity = capacity;
        this.elements = new Object[capacity];
    }

    public void reset() {
        this.writePos = 0;
        this.available = 0;
    }

    public int getCapacity() {
        return capacity;
    }

    public int getAvailable() {
        return available;
    }

    public int remainingCapacity() {
        return this.capacity - this.available;
    }

    public boolean put(Object element) {
        if (available < capacity) {
            if (writePos >= capacity) {
                writePos = 0;
            }
            elements[writePos] = element;
            writePos++;
            available++;
            return true;
        }
        return false;
    }

    public Object take() {
        if (available == 0) {
            return null;
        }
        int nextSlot = writePos - available;
        if (nextSlot < 0) {
            nextSlot += capacity;
        }
        Object nextObj = elements[nextSlot];
        available--;
        return nextObj;
    }

    public int put(Object[] newElements) {
        return put(newElements, newElements.length);
    }

    public int put(Object[] newElements, int length) {
        int readPos = 0;
        if (this.writePos > this.available) {
            //直接追加
            if (length <= this.capacity - this.writePos) {
                for (; readPos < length; readPos++) {
                    this.elements[this.writePos++] = newElements[readPos];
                }
                this.available += readPos;
                return length;
            } else {
                //需要在数组尾部和头部添加
                for (; this.writePos < this.capacity; this.writePos++) {
                    this.elements[this.writePos] = newElements[readPos++];
                }
                //重置写入下标
                this.writePos = 0;
                //计算还能写入多少
                int endPos = Math.min(length - readPos, capacity - available - readPos);
                for (; this.writePos < endPos; this.writePos++) {
                    this.elements[this.writePos] = newElements[readPos++];
                }
                this.available += readPos;
                return readPos;
            }
        } else {
            int endPos = this.capacity - this.available + this.writePos;
            for (; this.writePos < endPos; this.writePos++) {
                this.elements[this.writePos] = newElements[readPos++];
            }
            this.available += readPos;
            return readPos;
        }
    }

    public int take(Object[] into) {
        return take(into, into.length);
    }


    public int take(Object[] into, int length) {
        int intoPos = 0;

        if (available <= writePos) {
            int nextPos = writePos - available;
            int endPos = nextPos + Math.min(available, length);
            for (; nextPos < endPos; nextPos++) {
                into[intoPos++] = this.elements[nextPos];
            }
            this.available -= intoPos;
            return intoPos;
        } else {
            int nextPos = writePos - available + capacity;
            int leftInTop = capacity - nextPos;
            if (length <= leftInTop) {
                //copy directly
                for (; intoPos < length; intoPos++) {
                    into[intoPos] = this.elements[nextPos++];
                }
                this.available -= length;
                return length;
            } else {
                //copy top
                for (; nextPos < capacity; nextPos++) {
                    into[intoPos++] = this.elements[nextPos];
                }
                //copy bottom - from 0 to writePos
                nextPos = 0;
                int leftToCopy = length - intoPos;
                int endPos = Math.min(writePos, leftToCopy);
                for (; nextPos < endPos; nextPos++) {
                    into[intoPos++] = this.elements[nextPos];
                }
                this.available -= intoPos;
                return intoPos;
            }
        }
    }
}
//使用反转标记,实现RingBuffer
public class RingBufferFlipMarker {

    public Object[] elements = null;

    public int capacity = 0;
    public int writePos = 0;
    public int readPos = 0;
    public boolean flipped = false;

    public RingBufferFlipMarker(int capacity) {
        this.capacity = capacity;
        this.elements = new Object[capacity];
    }

    public void reset() {
        this.writePos = 0;
        this.readPos = 0;
        this.flipped = false;
    }

    public int remainingCapacity() {
        if (!flipped) {
            return capacity - writePos;
        }
        return readPos - writePos;
    }

    public int available() {
        if (!flipped) {
            return writePos - readPos;
        }
        return capacity - (writePos - readPos);
    }

    public boolean put(Object element) {
        if (!flipped) {
            if (writePos == capacity) {
                writePos = 0;
                flipped = true;
                if (writePos < readPos) {
                    elements[writePos++] = element;
                    return true;
                } else {
                    return false;
                }
            } else {
                elements[writePos++] = element;
                return true;
            }
        } else {
            if (writePos < readPos) {
                elements[writePos++] = element;
                return true;
            } else {
                return false;
            }
        }
    }

    public Object take() {
        if (!flipped) {
            if (readPos < writePos) {
                return elements[readPos++];
            } else {
                return null;
            }
        } else {
            if (readPos == capacity) {
                readPos = 0;
                flipped = false;
                if (readPos < writePos) {
                    return elements[readPos++];
                } else {
                    return null;
                }
            } else {
                return elements[readPos++];
            }
        }
    }

    public int put(Object[] newElements, int length) {
        int newElementsReadPos = 0;
        if (!flipped) {
            //readPos lower than writePos - free sections are:
            //1) from writePos to capacity
            //2) from 0 to readPos
            if (length <= capacity - writePos) {
                //new elements fit into top of elements array - copy directly
                for (; newElementsReadPos < length; newElementsReadPos++) {
                    this.elements[this.writePos++] = newElements[newElementsReadPos];
                }
                return newElementsReadPos;
            } else {
                //new elements must be divided between top and bottom of elements array
                //writing to top
                for (; this.writePos < capacity; this.writePos++) {
                    this.elements[this.writePos] = newElements[newElementsReadPos++];
                }
                //writing to bottom
                this.writePos = 0;
                this.flipped = true;
                int endPos = Math.min(this.readPos, length - newElementsReadPos);
                for (; this.writePos < endPos; this.writePos++) {
                    this.elements[writePos] = newElements[newElementsReadPos++];
                }
                return newElementsReadPos;
            }
        } else {
            //readPos higher than writePos - free sections are:
            //1) from writePos to readPos
            int endPos = Math.min(this.readPos, this.writePos + length);
            for (; this.writePos < endPos; this.writePos++) {
                this.elements[this.writePos] = newElements[newElementsReadPos++];
            }
            return newElementsReadPos;
        }
    }

    public int take(Object[] into, int length) {
        int intoWritePos = 0;
        if (!flipped) {
            //writePos higher than readPos - available section is writePos - readPos
            int endPos = Math.min(this.writePos, this.readPos + length);
            for (; this.readPos < endPos; this.readPos++) {
                into[intoWritePos++] = this.elements[this.readPos];
            }
            return intoWritePos;
        } else {
            //readPos higher than writePos - available sections are
            //top + bottom of elements array
            if (length <= capacity - readPos) {
                //length is lower than the elements available at the top
                //of the elements array - copy directly
                for (; intoWritePos < length; intoWritePos++) {
                    into[intoWritePos] = this.elements[this.readPos++];
                }
                return intoWritePos;
            } else {
                //length is higher than elements available at the top of the elements array
                //split copy into a copy from both top and bottom of elements array.
                //copy from top
                for (; this.readPos < capacity; this.readPos++) {
                    into[intoWritePos++] = this.elements[this.readPos];
                }
                //copy from bottom
                this.readPos = 0;
                this.flipped = false;
                int endPos = Math.min(this.writePos, length - intoWritePos);
                for (; this.readPos < endPos; this.readPos++) {
                    into[intoWritePos++] = this.elements[this.readPos];
                }
                return intoWritePos;
            }
        }
    }
}

数据分析 – 数据化汇报

场景:
1. 展示:数据分析结果可视化
2. 汇报:用数据向上级汇报工作成果
3. 说服:用数据高效说服别人

展示:
1.比较类图表:柱形图,条形图 描述数据对比关系
2.组合类图表:扇形图,饼图 描述数据的占比
3.描述类图表:散点图,气泡图 呈现数据分布情况

汇报:
向领导展示成绩 这件事情存在一些误区:
1.觉得领导什么都知道,其实他很难知道,管理者花一半的时间花在管理上面,这一半里面,一般有一多半在跟他的上级以及跨部门的沟通,所以他花在自己团队时间只有1/4,这些时间需要平均到6-8个下属,到个人只有3%,所以领导不知道你在干什么,需要跟他说,千里马常有而伯乐不常有,只是千里马不出众。
2.把苦劳当功劳,只说做了什么事,没有量化结果,价值不能量化,公司就不能给你定价,找到你的工作,与公司量化目标的关联。
公司的量化目标有哪些?无非就4类:
1.财务目标
流水,收入,利润,成本,现金流
2.客户目标
客户量,满意度,活跃度,转化率,复购率,品牌知名度,等等
3.运营目标
与公司价值链各项流程相关,比如研发周期,库存周转率,等等
4.组织目标
关键人才流失率,人才敬业度,人才匹配度

说服:

先看一个案例

公司销售经理,发现这个月市场投放过来的用户,很多不是目标用户,希望市场部同事能够调整渠道,以便获取更加精准的用户,来提升销售业绩,下面2种沟通方式:

第一种:

“这个月我们的销售目标没有完成,销售转化率下降了20%,接通率和愿意再聊的客户,平均下降了25%,我们发现市场部获取的目标用户不是很匹配,希望市场部同事能够调整策略,找到更匹配的客户渠道”

第二种:

“今年公司的整体销售目标,月度环比要增长10%,但是我们这个月的目标没有完成,做了全面分析之后,我们发现,虽然市场部提供的销售线索增加了10%,但是销售转化率却下降了20%,从过程指标来看,接通率和愿意再沟通的客户比例,平均下降了25%,这导致我们的销售在没有意向的客户上花费时间。我们需要更精准的目标客户,所以希望和市场团队就用户画像讨论一下,优化渠道策略”

第二种表达,多了一些故事起伏,不是单纯的列数据。

当我们在说服他人的时候,我们需要故事性表达,才能让对方集中注意以及产生认同,这里有一个常用的故事性表达结构:SCQA

SCQA 是四个英文单词缩写:

S是Situation 背景,也就是事情发生的背景信息是什么
C是Complication 复杂性,也就是实际情况和理想情况的差距是什么
Q是Question 问题,也就是当务之急需要解决什么问题
A是Answer 答案,我提出的解决方案是什么

在SCQA的每个要素之下,都有一个观点,把你数据分析的结果,数据可视化图表呈现在这个观点下面,去支持你的观点。

总结:

1)完全一致的数据,改变展现方式,就会改变和扭转一个人对数据的解读和判断

2)工作中常用三种图表

3)通过把自己工作和公司目标关联,量化自己的工作,才能让老板认识到你的价值

4)公司目标可以从这4个方面来找:财务目标,客户目标,运营目标,组织目标

5)与同事沟通时,通过SCQA 故事框架,用数据来佐证框架中的每个观点,就能更有说服力

6)学会使用SCQA来表达

数据分析 – 相关分析法

数据分析 – 相关分析法

目前我们已经知道了,在工作中,我们可以用对比分析来发现并定位问题;用拆解分析,将复杂问题简单化,帮我们找到小的切入点。

但是这样就会存在,同一个问题,我们有时候会找到很多小的切入点。

这个时候需要相关分析,来使用数据判断这些因素是否和最终结果相关,以及相关密切程度高低,据此判断工作优先级。

相关的定义?什么是相关?

相关,就是指两个变量,当一个变量发生变化的时候,另一个变量也跟着变化。比如:工资随着工作年限发生变化,销售量随着价格发生变化,体重随着身高发生变化,等等。

相关分析定义?什么是相关分析?

相关分析,就是去找到两种变量之间的关系。也就是B是怎么随着A的变化产生变化的。

怎么使用相关分析? 相关分析的三大场景:

1、快速锁定大问题相关的一个个小因素,

案例:一位TED演讲嘉宾,她想知道线上交友网站上,怎么设置自己信息才能最好的呈现自己,让自己在社交网站上非常受欢迎。于是,她收集了大量女性账户的行为数据,包含幽默感,语调,声音,沟通方式,个人描述的平均长度,与男性发私信之间隔多久等等。 然后,把这些数据和对应女性受欢迎的程度进行相关分析。发现:最受欢迎的女性都在用诸如“有趣”,“女孩“,”爱“ 这样的关键词,自我介绍一般97个词,以及她们会等23个小时进行下一次沟通。掌握这些信息之后,她建立了一个超级档案,还是本人真实信息,按照分析结果,重新包装,结果她成了网站最受欢迎的人。而且在众多男性里面发现了潜在的”白马王子“,三年之后他们结婚了。

2、用数据说服他人,终结撕逼

除了给自己的工作进行问题分析与定位之外,相关分析可以用在沟通方面,比如:当你在和领导汇报的时候,常常就一件事情进行原因阐述和分析,这个时候每个人都有自己经验和主见,很难达成一致结果,这时候使用相关分析进行验证,就很容易有说服力。

案例:市场部负责投放市场预算在一些渠道,来拉目标用户,而销售负责把产品卖给用户。这个时候,市场和销售会经常撕逼,销售会说:因为市场渠道质量不行,导致销售业绩差,市场会说:销售能力不行,我的渠道质量杠杠的。这个时候就需要采用相关分析,对渠道和销售业绩做一个相关分析,用数据说话,很有说服力。

3、帮我们判断事情优先级

比如,影响销量有非常多的因素,要提升销量,应该做那个?我们就看相关度最高的,因为相关分析得出一个相关系数,根据分值高低判断相关程度,这样就能确定最相关因素,来优先改进和提升。

那么相关分析怎么用了?相关都有哪些类型?

相关可以分为:线性相关,非线性相关

线性相关: y = ax + b , 相关程度就是求斜率。
可以使用Excel或者Python 画出散点图,

相关度R定义: (求出斜率之后 计算数据的拟合度)
1.绝对值大小: 表示相关程度
2.正负:表示正相关还是负相关
3.数值范围:-1 ~ 1
4.数值定义:-1表示负相关,1表示相关,0表示非线性相关(可能其他形式相关)

相关度经验值:
1.绝对值 R >= 0.6 高度相关
2.绝对值 0.3 =< R < 0.6 中度相关
3.绝对值 R < 0.3 弱相关

经典案例:
谷歌收集了美国从2003 到 2008 年期间,流感传播时间和地点,匹配当地,当时人们使用谷歌搜索关键词,寻找其中关系。成功预测了2009冬季流感的传播趋势,甚至可以精确预测流感发生的地区。 根据搜索词语和流感传播关系,建立数据模型,能够提前2周预测大规模流感传播,让人们提前预防。

Facebook,People You May Know, 好友推荐,“你可能认识的人”,这个习以为常的功能,是facebook提升用户留存,进行相关分析,发现有更多的好友,用户粘性越高,越活跃。

相关分析应用范围很广,但是不能滥用,比如我们分析孩子长高的原因不能联想到院子里面的树也长高了。二者虽然都长高了,但是二者并没有相关。

总结:

1)分析问题本质就是对大问题拆解
2)相关分析,去找到2个变量之间的关系
3)相关分析三个场景:1.锁定大问题相关因素 2.数据说服,停止撕逼 3.判断事情优先级
4)了解应用场景,知行合一才能完美解决问题
5)相关分为:线性和非线性
6)相关分析可以做预测
7)因果关系才是相关分析的目标

数据分析-漏斗分析法

漏斗分析法:对问题进行拆解,将复杂问题简单化

分析问题的本质,其实就是把一个大问题进行拆解,把一个大问题拆解成一个个小问题。

案例:
怎么分析下单量下降了。
可以想一些在线购物的流程?你就能想到:一个用户购买商品,会经历几个环节,包括看到广告、点进商品页、点击购买、完成支付等等,最后的订单量下降,我们需要分析那个环节的数据下降了。通过这个方式,你就可以把一个终极的大问题,顺着环节、流程、拆解位一个个过程问题。

漏斗分析案例:你需要认识多少个适龄单身异性才能达到今年脱单的目标。
我们根据恋爱的步骤建立数据漏斗:相遇、相知、相处、相爱。估算一下每个环节的转化率是多少,然后就可以计算出来,最终脱单需要和多少人相遇。

漏斗分析,适用于流程较长、环节较多,并且随着环节进行流量逐渐流失的场景。

漏斗分析的3个应用场景:
1.分析拆解问题,直观的发现和说明问题所在
2.帮助我们分配精力,发现问题后集中尽力解决问题
3.绩效目标管理,比如:一个管理者,需要给团队指定绩效目标,如果定一个笼统的销售额,下属很难完成,因为他很难有能力去思考,到底做什么事情可以提升销售额,所以如果你用漏斗分析出来,我们目前的销售最大的转换问题是用户从试用到下单的环节,那么目标可以不定成销售额,而是定为试用转化率。

漏斗分析三个步骤:
1.根据工作流画出漏斗的各个环节路径
2.对比漏斗各环节做数据分析
3.确定那个环节作为优化重点

思考一下:从流程来看,招聘流程节点有哪些?
我们能够想到的有:发布招聘信息,接受简历,简历初筛,笔试,邀约面试,HR面试,部门经理面,高管面,发Offer,入职,通过试用期。

好,那是不是我们要把这些环节,画成漏斗的形状,形成一个招聘漏斗。
其实不然,我们发现,整个流程节点一共11个,但是如果我们在做漏斗分析的时候,如果把这些节点都放进去,会导致环节过多,反而不利于聚焦。

这里有一个指导原则,就是我们把流程节点转换为漏斗的时候,漏斗的层数最后不要超过7个。
1.归并:对一些节点进行归并。比如,几轮面试可以合并为一层
2.切割:切成不同的漏斗。比如:把发布招聘信息到收到简历,切割为另一个漏斗,比如叫做“简历漏斗”,然后把从邀约到最后入职,切割成一个漏斗,叫做“面试漏斗”

当然,除了自己去思考,实际在各个领域都有固定的漏斗模型,可以直接拿来用。下面是几种常用的漏斗模型。

漏斗1.消费者行为漏斗,简称AIDA模型。AIDA是4个英文单词的首字母,分别对应Attention 注意,Interest 兴趣,Desire 欲望,Action 行为。这个模型意思是,当你希望用户购买某个产品和服务时,应该先吸引他的注意力,再引起他的兴趣,然后勾起其他的欲望,最后促成他行动。

比如:拼多多的广告,先用一个非常洗脑的广告引起注意,“3亿人都在用”这样的广告勾起兴趣,继而用便宜价格激发购买欲望,最后用一些限时拼单等等有紧迫感的活动,促进下单。

漏斗2.用户增长漏斗(海盗模型),用户增长漏斗AARRR模型,近年在互联网领域异常火爆的模型,又被称为“海盗模型”。AARRR,实际上是5个英文单词的首字母,分别代表了Acquisition 用户获取,Activation 促活,Retention 用户留存,Revenue 转化,Referral 转介绍 推荐转发 这5个步骤。

比如:你通过某个广告知道了淘宝,并下载注册了淘宝账户,这个呢,就叫用户获取,注册之后,淘宝会通过推送通知消息,短信等等,来不断刺激你登录,这就要用户促活,主要目的就是希望你不要注册了就再也不来。然后淘宝会通过各种内容,比如直播,会员等等形式,增强你和淘宝的粘性,这个叫用户留存。接着,它还会根据算法,推荐你喜欢的商品,或者推送优惠信息,促使下单,这个叫转化,最后,淘宝通过分享功能,来促使你吧平台商品转发出去,带来新用户。

一些互联网产品,只需要在某个环节做到极致,就可以快速增长,比如拼多多,就是通过拼单的方式,把最后一步转介绍做到极致,从而获取了告诉增长。

漏斗3.营销广告投放漏斗

展示量,点击量,访问量,咨询量,成交量

广告营销界又这样一句话:“我知道花在广告上的钱,一半是浪费的,问题是,我不知道是那一半。”
这种情况,在这个时代,已经越来越少了,每个推广渠道,都可以看见曝光量,用户点击量,用户注册量,甚至购买量,等等。市场人员可以通过各个渠道的数据追踪和分析。及时优化渠道的选择和广告内容。根据这个营销漏斗定位问题,要么优化产品的落地页,要么增强目标投放人群筛选的精准度等,把广告预算花在优质的、有效的渠道。

拆解完漏斗之后,怎么发现问题,单纯的漏斗数据很难发现问题,我们需要通过对比才能发现问题。

历史对比:发现数据和过去又差异,需要回顾 最近做了什么动作,结合历史行为进行分析。
外部对比:发现和外部的差异,能够让我们定位问题和分配精力
横向对比:可以针对不同群体,分别做漏斗数据

做完漏斗数据分析对比,我们可以得到一些结论了,接下来我们要做的是,找到最值的优化的环节。因为找到最值的优化提升的环节,把大问题拆解为更加精准的小问题,才是漏斗分析的最大价值,它可以帮我们聚焦。那么怎么聚焦呢?非常简单,我们使用下面指标来衡量:
1.那个环节优化产出最高
2.优化投入更低

好了,那么到现在为止,一整套分析问题的方法已经形成,总结几个关键点:

1)漏斗分析的本质,是一套分析流程,适用于流程较长,环节较多,并且随着环节进行,流量逐渐流失的场景

2)应用场景:分析拆解问题、帮我们分配精力、绩效管理

3)漏斗分析有三个步骤
步骤一:根据工作流画出漏斗的各个环节路径
步骤二:对漏斗各个环节做数据分析
步骤三:确定那个环节作为优化重点

4)画漏斗的时候,层数最好不超过7层,可以通过归并和切割达到这个要求

5)漏斗数据分析会使用对比的方法:历史对比、横向对比、外部对比

6)确定聚焦那个环节优化的时候,参考2个原则:那个环节优化产出高,以及优化的成本更低

数据分析-发现问题

从数据中发现问题,获取洞见,核心就是对比。

既然要对比数据,就需要回答3个问题。
1、对比什么数据?(what)
2、怎么对比?(how)
3、和谁对比?(who)

数据四大来源
1、从部门KPI和你自己的KPI找
2、从你的工作流里面找
3、从相关的行业报告/行业分析里面找
4、从最近的Excel表格文件找

数据的统计指标和应用
1、平均数、中位数、众数 用来描述数据的集中趋势
2、最值、最大值、最小值 用来描述数据的极端情况
3、比率、比例 用来描述数据比值

如果各个数据之间的差异程度较小,那适合用平均数;如果数据之前差异度较大,或者存在个别的极端值,那用中位数或者众数,更具有代表性。
如果你不知道用什么怎么办?那么就都用上,因为:越多的维度可以对比,就有机会得到更多的洞见。有时候只看绝对数往往无法分析,或者得到的结论是错误的。这时候,比率更能提供指导意见。

和谁对比,常见的对比对象也有3种:历史对比、横向对比、外部对比。
历史对比:用当下的数据,与过去的数据,进行纵向对比。
横向对比:通过横向对比其他要素的情况,来发现和诊断问题。
外部对比:历史和横向对比 都集中在自身或者公司内部,很多时候对比竞争对手可以获取更多的信息。

领导力的四个阶段

The Culture of Leadership

第一阶段:熟悉自己的业务,知道问题在哪里,怎样可以解决。

领导者是给大家指方向的,你必须先知道要走哪个方向,才能带领别人,这是领导力的基础。

第二阶段:培养说服能力,能说服他人,问题可以按照你说的方式解决。

领导力的表现是,他人愿意服从你。这不能都靠制度压服,而要让他人真心觉得你是对的。如果你可以让他人相信你,你就可以领导。

第三阶段:激发他人的热情,让他们产生解决问题的热情。

说服他人的更高境界,是让他们真正投身进来,自觉发挥自己的潜力,全力以赴解决需要解决的问题。好的领导和差的领导,区别就在于能否激励下级,让每个人都知道自己的职责,努力工作。

第四阶段:你培养其他人的领导能力。

如果你离开,团队也能正常运作下去,说明一切已经制度化了,你的领导已经成功了。这时,你可以让其他人接管团队,自己去实现下一个目标。