Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

LeetCode-153:Find Minimum in Rotated Sorted Array (可能旋转的排序数组中的最小值) -- medium

$
0
0

Question

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Find the minimum element.

note:

  • You may assume no duplicate exists in the array.

问题解析:

给定一个原本按照升序排序的数字,但在输入之前在某个结点上进行了数组的旋转,找到数组的最小值。

Answer

Solution 1:

二分法。

  • 因为给定的数组是按照升序排序好的,所以num[0]就是数组的最小值。但是在输入之前可能会在某个结点进行旋转,旋转之后最低位一定是大于最高位的;
  • 下面以旋转后进行分析:以二分法进行判断,如果中间位置大于高位,那么说明最小值处于midhigh之间;否则,说明最小值处于lowmid之间。
  • 一旦low位置的值小于high位置的值,也就是已经找到升序排序的子数组,那么low位置的值就是整个数组的最小值。
class Solution {
    public int findMin(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        if (nums.length == 1) return nums[0];

        int low = 0, high = nums.length - 1;
        while (nums[low] > nums[high]){
            int mid = (low + high) / 2;
            if (nums[mid] > nums[high]) low = mid + 1;
            else high = mid;
        }

        return nums[low];
    }
}
  • 时间复杂度:O(lgn),空间复杂度:O(1)
作者:Koala_Tree 发表于2017/11/8 12:38:31 原文链接
阅读:7 评论:0 查看评论

2013ACM/ICPC亚洲区南京站现场赛——题目重现 (HDU 4810) 二进制思想

$
0
0

Wall Painting

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3563    Accepted Submission(s): 1172


Problem Description
Ms.Fang loves painting very much. She paints GFW(Great Funny Wall) every day. Every day before painting, she produces a wonderful color of pigments by mixing water and some bags of pigments. On the K-th day, she will select K specific bags of pigments and mix them to get a color of pigments which she will use that day. When she mixes a bag of pigments with color A and a bag of pigments with color B, she will get pigments with color A xor B.
When she mixes two bags of pigments with the same color, she will get color zero for some strange reasons. Now, her husband Mr.Fang has no idea about which K bags of pigments Ms.Fang will select on the K-th day. He wonders the sum of the colors Ms.Fang will get with different plans.

For example, assume n = 3, K = 2 and three bags of pigments with color 2, 1, 2. She can get color 3, 3, 0 with 3 different plans. In this instance, the answer Mr.Fang wants to get on the second day is 3 + 3 + 0 = 6.
Mr.Fang is so busy that he doesn’t want to spend too much time on it. Can you help him?
You should tell Mr.Fang the answer from the first day to the n-th day.
 

Input
There are several test cases, please process till EOF.
For each test case, the first line contains a single integer N(1 <= N <= 103).The second line contains N integers. The i-th integer represents the color of the pigments in the i-th bag.
 

Output
For each test case, output N integers in a line representing the answers(mod 106 +3) from the first day to the n-th day.
 

Sample Input
4 1 2 10 1
 

Sample Output
14 36 30 8
 


【理解】

第k天 取 k 个数 亦或,  结果 加起来 作为第 k 的 答案,

立即想到 组合数,  杨辉三角;

单纯模拟的话, 不好模拟,  k 越大  取得数越多, 不知道 取哪一个;

想了下,  亦或性质,  遇到亦或的 题 总能跟二进制扯上关系,   亦或 1 0  才取 1   1 的个数 只有在 奇数 才会有贡献;

1     0001

2     0010

      10     1010

         1    0001                   1 0 2 2     用 组合数理解为,   转换成十进制,  k=1:   C(3,3) * C (1 .1)* 2^3  +   C(2,1)*(2,0) *2 ^1  +  C(2,1)*C(2,0)* 2^1  ==14




【代码】

注意 运算乘法 会爆 int  用long long int

//#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <cstring>
#include <string>
#include <queue>
#include <deque>
#include <stack>
#include <stdlib.h>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <vector>
#define mem(a,b) memset(a,b,sizeof(a))
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define FIN      freopen("input.txt","r",stdin)
#define FOUT     freopen("output.txt","w",stdout)
#define S1(n)    scanf("%d",&n)
#define SL1(n)   scanf("%I64d",&n)
#define S2(n,m)  scanf("%d%d",&n,&m)
#define SL2(n,m)  scanf("%I64d%I64d",&n,&m)
#define Pr(n)     printf("%d\n",n)
#define lson rt << 1, l, mid
#define rson rt << 1|1, mid + 1, r

using namespace std;
typedef long long ll;
const double PI=acos(-1);
const int INF=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=1e6+5;
const int MAXN=50005;
const int MOD=1e6+3;
const int mod=1e6+3;
int dir[5][2]={0,1,0,-1,1,0,-1,0};

ll inv[maxn*2];
inline void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){ x=1; y=0; d=a; }else{ ex_gcd(b,a%b,d,y,x); y-=x*(a/b);};}
inline ll gcd(ll a,ll b){ return b?gcd(b,a%b):a;}
inline ll exgcd(ll a,ll b,ll &x,ll &y){if(!b){x=1;y=0;return a;}ll ans=exgcd(b,a%b,x,y);ll temp=x;x=y;y=temp-a/b*y;return ans;}
inline ll lcm(ll a,ll b){ return b/gcd(a,b)*a;}
inline ll qpow(ll x,ll n){ll res=1;for(;n;n>>=1){if(n&1)res=(res*x)%MOD;x=(x*x)%MOD;}return res;}
inline ll inv_exgcd(ll a,ll n){ll d,x,y;ex_gcd(a,n,d,x,y);return d==1?(x+n)%n:-1;}
inline ll inv1(ll b){return b==1?1:(MOD-MOD/b)*inv1(MOD%b)%MOD;}
inline ll inv2(ll b){return qpow(b,MOD-2);}

int c[3010][3010];
void init()
{
    c[0][0]=1;
    for(int i=1;i<=1000;i++)
        for(int j=0;j<=i;j++)
        {
            c[i][j]= ((j==0)? 1: (c[i-1][j]+c[i-1][j-1])%mod);
        }

}
int ans[MAXN];
int num[64];
int main()
{
    int N;
    init();
    while(~scanf("%d",&N))
    {
        mem(ans,0);
        mem(num,0);
        int x;
        for(int i=1;i<=N;i++)
        {
             scanf("%d",&x);
             for(int i=0;i<=30;i++)
                //if( (1<<(i-1))&x )// 统计1 的个数
                  if( (x>>i)&1)
                    num[i]++;
        }
        for(int i=1;i<=N;i++)
        {
            for(int j=0;j<=30;j++)
                for(int k=1;k<=num[j]&&k<=i;k+=2)// 奇数贡献
                {
                    //printf("%d  %d  %d \n",c[num[j]][k],c[N-num[j]][i-k],1<<j);
                    ans[i]= (ans[i] + (ll) (((1<<j)%mod)*(ll)c[num[j]][k]%mod )*(ll)c[N-num[j]][i-k]%mod)%mod;
                }

        }
        printf("%d",ans[1]%mod);
        for(int i=2;i<=N;i++)
            printf(" %d",ans[i]%mod);
        printf("\n");
    }

    return 0;
}


123

作者:sizaif 发表于2017/11/8 13:00:47 原文链接
阅读:10 评论:0 查看评论

《剑指offer》刷题笔记(分解让复杂问题简单):复杂链表的复制

$
0
0

《剑指offer》刷题笔记(分解让复杂问题简单):复杂链表的复制



前言

在计算机领域有一类算法叫分治法,即“分而治之”。采用的就是各个击破的思想,我们把分解后的小问题各个解决,然后把小问题的解决方案结合起来解决大问题。

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

解题思路

我们可以将复杂链表的复制过程分解为三个步骤。在写代码的时候我们每一步定义一个函数,这样每个函数完成一个功能,整个过程的逻辑也就非常清晰明了了。

大部分人首先想到的可能是先复制复杂指针的label和next,然后再查找random并更新。查找random又分为两种,一种是每次都从头查找,时间复杂度为O(n^2);另一种是空间换时间,复制label和next的同时建立一个hash表来存放新旧复杂指针的对应关系,所以后续只需一步就能找到random,算法时间复杂度为O(n)。

我们这里采用三步走战略,也是剑指offer上推崇的方法:

  • 第一步:复制复杂指针的label和next。但是这次我们把复制的结点跟在元结点后面,而不是直接创建新的链表;
  • 第二步:设置复制出来的结点的random。因为新旧结点是前后对应关系,所以也是一步就能找到random;
  • 第三步:拆分链表。奇数是原链表,偶数是复制的链表。

有图思路更清晰:

C++版代码实现

/*
/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    //第一步,复制复杂指针的label和next
    void CloneNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = new RandomListNode(0);
            pCloned->label = pNode->label;
            pCloned->next = pNode->next;
            pCloned->random = NULL;

            pNode->next = pCloned;
            pNode = pCloned->next;
        }
    }
    //第二步,处理复杂指针的random
    void ConnectSiblingNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = pNode->next;
            if(pNode->random != NULL)
                pCloned->random = pNode->random->next;
            pNode = pCloned->next;
        }
    }
    //第三步,拆分复杂指针
    RandomListNode* ReconnectNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        RandomListNode* pCloneHead = NULL;
        RandomListNode* pCloneNode = NULL;
        if(pNode != NULL){
            pCloneHead = pCloneNode = pNode->next;
            pNode->next = pCloneNode->next;
            pNode = pNode->next;
        }
        while(pNode != NULL){
            pCloneNode->next = pNode->next;
            pCloneNode = pCloneNode->next;
            pNode->next = pCloneNode->next;
            pNode = pNode->next;
        }
        return pCloneHead;
    }

    RandomListNode* Clone(RandomListNode* pHead)
    {
        CloneNodes(pHead);
        ConnectSiblingNodes(pHead);
        return ReconnectNodes(pHead);
    }
};

Python版代码实现

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    #RandomListNode
    def CloneNodes(self, pHead):
        pNode = pHead
        while pNode is not None:
            pCloned = RandomListNode(0)
            pCloned.label = pNode.label
            pCloned.next = pNode.next
            pCloned.random = None

            pNode.next = pCloned
            pNode = pCloned.next

    def ConnectSiblingNodes(self, pHead):
        pNode = pHead
        while pNode is not None:
            pCloned = pNode.next
            if pNode.random is not None:
                pCloned.random = pNode.random.next
            pNode = pCloned.next

    def ReconnectNodes(self, pHead):
        pNode = pHead
        pCloneHead = None
        if pNode is not None:
            pCloneHead = pCloneNode = pNode.next
            pNode.next = pCloneNode.next
            pNode = pNode.next
        while pNode is not None:
            pCloneNode.next = pNode.next
            pCloneNode = pCloneNode.next
            pNode.next = pCloneNode.next
            pNode = pNode.next
        return pCloneHead

    def Clone(self, pHead):
        # write code here
        self.CloneNodes(pHead)
        self.ConnectSiblingNodes(pHead)
        return self.ReconnectNodes(pHead)

系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~

完的汪(∪。∪)。。。zzz

作者:u011475210 发表于2017/11/8 13:31:11 原文链接
阅读:17 评论:0 查看评论

Quartz-Java Web项目中使用Quartz

$
0
0

概述

Quartz也常用在Web应用中,常见的是交由Spring托管的形式,但这里并非介绍这个。如果你的很老的一个项目没有使用Spring呢? 这里我们介绍Quartz在Web应用中单独使用的场景。


实现

对于定时任务来讲,一般来说,Web应用启动时,应注册已经确定的定时任务;一些动态的、未确定触发时间的定时任务,后续可通过静态的Scheduler注册。

这里使用监听器在应用启动时注册,需要在web.xml注册这个监听器,在关闭Web应用时,也要相应的注销定时任务。


示例

maven工程

这里写图片描述


步骤一 构建Maven项目

pom.xml中添加依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.artisan</groupId>
    <artifactId>quartzInWeb</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>quartzInWeb Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency>

        <!-- 日志组件slf4j xml的方式需增加 logback -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.7</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

    </dependencies>
    <build>
        <finalName>quartzInWeb</finalName>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

步骤二 日志组件的配置logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 应用名称 -->
    <property name="APP_NAME" value="logtest" />
    <!--日志文件的保存路径,首先查找系统属性-Dlog.dir,如果存在就使用其;否则,在当前目录下创建名为logs目录做日志存放的目录 -->
    <property name="LOG_HOME" value="${log.dir:-logs}/${APP_NAME}" />
    <!-- 日志输出格式 -->
    <property name="ENCODER_PATTERN"
              value="%d{yyyy-MM-dd  HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n" />
    <contextName>${APP_NAME}</contextName>

    <!-- 控制台日志:输出全部日志到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>${ENCODER_PATTERN}</Pattern>
        </encoder>
    </appender>

    <!-- 文件日志:输出全部日志到文件 -->
    <appender name="FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/output.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 错误日志:用于将错误日志输出到独立文件 -->
    <appender name="ERROR_FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
    </appender>

    <!-- 独立输出的同步日志 -->
    <appender name="SYNC_FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/sync.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
    </appender>

    <logger name="log.sync" level="DEBUG" addtivity="true">
        <appender-ref ref="SYNC_FILE" />
    </logger>

    <root>
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>
</configuration>

步骤三 自定义监听器的编写

package com.artisan.quartz;

import static org.quartz.JobBuilder.newJob;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ApplicationContextListener implements ServletContextListener {
    private Logger logger = LoggerFactory
            .getLogger(ApplicationContextListener.class);

    public static Scheduler scheduler = null;

    public void contextInitialized(ServletContextEvent servletContextEvent) {
        logger.info("Web应用开始...");

        /* 注册定时任务 */
        try {
            // 获取Scheduler实例
            scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.start();

            // 具体任务
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1", "group1").build();

            // 触发时间点
            SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder
                    .simpleSchedule().withIntervalInSeconds(5).repeatForever();

            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger1", "group1").startNow()
                    .withSchedule(simpleScheduleBuilder).build();

            // 交由Scheduler安排触发
            scheduler.scheduleJob(job, trigger);

            logger.info("调度器开始注册:The scheduler register...");
        } catch (SchedulerException se) {
            logger.error(se.getMessage(), se);
        }
    }

    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("Web应用停止...");

        /* 注销定时任务 */
        try {
            // 关闭Scheduler
            scheduler.shutdown();

            logger.info("调度器已关闭:The scheduler shutdown...");
        } catch (SchedulerException se) {
            logger.error(se.getMessage(), se);
        }
    }

}

假设我们有一个明确的任务,在初始化监听器的时候就启动执行,如下

package com.artisan.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloJob implements Job {

    private Logger logger = LoggerFactory.getLogger(HelloJob.class);

    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        System.out.println("Hello Job");
        // 此任务仅打印日志便于调试、观察
        System.out.println(this.getClass().getSimpleName() + " trigger...");
        logger.debug(this.getClass().getSimpleName() + " trigger...");
    }

}

步骤四 web.xml中注册监听器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- 加入自定义监听器 -->
    <listener>
        <listener-class>com.artisan.quartz.ApplicationContextListener</listener-class>
    </listener>

</web-app>

步骤五 启动

由于我们使用的JDK1.7 ,我们用的tomcat,这里tomcat的版本需要为8.0

这里写图片描述

关键日志如下:

这里写图片描述

如果我们Eclipse或者Spring tool suit中调试,无法看到contextDestroyed方法的执行。

SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever();

repeatForever()方法,这个方法的意思表示永远执行,当然我们也可以自定义重复执行的次数,使用withRepeatCount(10)方法,10表示执行了10次

作者:yangshangwei 发表于2017/11/8 13:51:19 原文链接
阅读:0 评论:0 查看评论

关联分析之Apriori算法

$
0
0

本文demo源码、实验数据:传送门

引言

如题,关联分析这个词语对于初学者而言或许比较陌生。但是我若将关联分析换成另一个短语“尿布与啤酒”大家就会很熟悉了。据报道,美国中西部的一家连锁店发现,男人们会在周四购买尿布和啤酒。这样商店实际上可以将尿布和啤酒放在一块,并确保在周四的销售中获利。“尿布与啤酒”是关联分析中最著名的例子。那么关联分析的定义也就呼之欲出了:从大规模数据集中寻找物品间的隐含关系被称作关联分析或者关联规则学习。但是寻找物品之间的隐含关系是一项十分耗时的任务,所需的计算代价很高,这就催生出了Apriori算法来在合理的时间范围内解决上述问题。

频繁项集、关联分析

关联分析是一种在大规模数据集中寻找有趣关系的任务。这些关系可以有两种形式:频繁项集或关联规则。频繁项集是经常出现在一起的物品的集合,关联规则按时两种物品之间可能存在很强的关系。我们来举个例子来说明这两个概念。下图给出了某个杂货店的交易清单。
grocery store
频繁项是指那些经常出现在一起的物品集合,如上图所示的{diapers,wine}就是频繁项集的一个例子。从上图中我们也能找到诸如diaperswine的关联规则。这意味着如果有人买了diapers,那么他很可能也会买wine。

量化的方法–支持度、置信度

如何判断数据集中存在这种关系,如何量化数据从而得到判断这些关系是否存在的标准呢?有人提出了支持度(support)和置信度(confidence)的概念。
一个项集的支持度被定义为数据集中包含该项的记录所占的比例。如上图所示,{soy milk}的支持度为4/5。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。置信度是针对诸如diaperswine的关联规则来定义的。这条规则的置信度被定义为:支持度({diapers,wine})/支持度({diapers})。这一定义也可以用条件概率来解释:

P(diapers,wine)=P(wine|diapers)P(diapers)

大家看到这个条件概率是不是很惊奇?其实从apriori这里我们就能发现一丝端倪了。Apriori在拉丁语中指”来自以前”。当定义问题时,通常会使用先验知识或者假设,这被称作“一个先验”(apriori)。在贝叶斯统计中,使用先验知识作为条件进行推断也很正常。
支持度和置信度是用来量化关联分析是否成功的方法。假设想找到支持度大于0.8的所有项集,应该如何去做?或许有人会想到“贪心”的方法,就是生成一个物品所有可能组合的清单,然后对每一种组合统计它出现的频繁程度,但是当物品成千上万时,上述做法就需要消耗大量的计算资源。因此有人提出了Apriori算法来减少关联规则学习时所需的计算量。

Apriori原理

我们以经营一家商品种类并不多的杂货店为例。我们总共有四种商品:商品0,商品1,商品2,商品3。那么这四种商品所有可能的项集组合如下所示:
possible
对于仅有4种物品的集合,也要有15种项集组合。对于N种物品的集合,就有2N1种项集组合。
这种项集组合个数指数型增长将会消耗极大的计算资源。那么,到底有没有一种方法能够降低所需要的计算时间呢?

Apriori原理

研究人员发现了一种所谓的Apriori原理。这种原理可以帮助我们减少可能感兴趣的项集。为什么Apriori原理能够减少计算时间呢?那是因为在数据中,如果某个项集是频繁的,那么它的所有子集都是频繁地。反之,如果一个项集是非频繁的,那么它的所有超集也》
association
上图给出了所有可能的项集,其中非频繁项用灰色表示。由于集合{2,3}是非频繁的,因此{0,2,3}、{1,2,3}、{0,1,2,3}也是非频繁的,它们的支持度根本不需要计算。

使用Apriori算法来发现频繁项集

前面我们有说,关联分析的目标包括两项:发现频繁项集和发现关联规则。首先需要先找到频繁项集,然后才能获得关联规则。
Apriori算法是发现频繁项集的一种方法。Apriori算法的两个输入参数分别是最小支持度和数据集。该算法首先会生成所有单个物品的项集列表。接着扫描交易记录来查看哪些项集满足最小支持度的要求,那些不满足最小支持度的集合会被去掉。然后,对剩下来的集合进行组合以生成包含两个元素的项集。接下来,再重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到满足退出条件。

生成候选项集

下面会创建一个用于构建初始集合的函数,也会创建一个通过扫描数据集以寻找交易记录子集的函数。数据集扫描的伪代码如下:
这里写图片描述
实际代码如下:

def loadDataSet():
    return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]

def createC1(dataSet):
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])

    C1.sort()
    return list(map(frozenset, C1))#use frozen set so we
                            #can use it as a key in a dict    

def scanD(D, Ck, minSupport):
    ssCnt = {}
    for tid in D:
        for can in Ck:
            if can.issubset(tid):
                if not can in ssCnt: ssCnt[can]=1
                else: ssCnt[can] += 1
    numItems = float(len(D))
    retList = []
    supportData = {}
    for key in ssCnt:
        support = ssCnt[key]/numItems
        if support >= minSupport:
            retList.insert(0,key)
        supportData[key] = support
    return retList, supportData

我们一共有三个函数,第一个函数loadDataSet()
用来构建交易记录,一共四条交易记录,商品种类一共5种。第二个函数是createC1()这里C1是指大小为1的所有候选项集的集合。第三个函数scanD(),它有三个参数,分别是数据集、候选集列表Ck以及感兴趣项集的最小支持度minSupport。
我们来看实际的运行效果:

import importlib
import apriori
dataSet = apriori.loadDataSet()
dataSet
[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
importlib.reload(apriori) 
C1 = apriori.createC1(dataSet)
C1
[frozenset({1}),
 frozenset({2}),
 frozenset({3}),
 frozenset({4}),
 frozenset({5})]
L1 ,suppData0  = apriori.scanD(dataSet,C1,0.5)
L1
[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]

上述四个项集构成了L1列表,该列表的每个但物品项集至少出现在50%以上的记录中。这通过去掉物品4,从而为接下来寻找两两物品组合的项集减少了工作量。

完整的Apriori算法

整个Apriori算法的伪代码如下:
algorithm
完整的代码如下:

def aprioriGen(Lk, k): #creates Ck
    retList = []
    lenLk = len(Lk)
    for i in range(lenLk):
        for j in range(i+1, lenLk): 
            L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2]
            L1.sort(); L2.sort()
            if L1==L2: #if first k-2 elements are equal
                retList.append(Lk[i] | Lk[j]) #set union
    return retList

def apriori(dataSet, minSupport = 0.5):
    C1 = createC1(dataSet)
    D = dataSet
    L1, supportData = scanD(D, C1, minSupport)
    L = [L1]
    k = 2
    while (len(L[k-2]) > 0):
        Ck = aprioriGen(L[k-2], k)
        Lk, supK = scanD(D, Ck, minSupport)#scan DB to get Lk
        supportData.update(supK)
        L.append(Lk)
        k += 1
    return L, supportData

上述代码由两个函数组成,主函数是apriori(),它会调用aprioriGen()来创建候选集Ck。其中有句代码需要注意下:

L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2]

这里的k-2或许大家不是很清楚,k是指要生成的元素个数,假设有项集{0,1},{0,2},{1,2}那么如果两两集合合并,就会得到重复项,为了减少遍历列表时的时间复杂度,我们发现如果比较集合的前k-2个元素是否相等,相等便合并,不想等不合并。按这个规则合并,不仅结果一样,而且没有重复计算的过程。
具体实验结果如下:

##apriori algorithm
importlib.reload(apriori) 
L,suppData = apriori.apriori(dataSet)
L
[[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})],
 [frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})],
 [frozenset({2, 3, 5})],
 []]
L[0]
[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]
L[1]
[frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})]
L[2]
[frozenset({2, 3, 5})]
L[3]
[]

从频繁项集中挖掘关联规则

前面我们介绍了Apriori原理,然后实现了Apriori算法来发现频繁项集,现在我们就来找出关联规则。要想找到关联规则,首先就要从频繁项集入手。我们希望从某个元素或者某个元素集合去推导出另一个元素或集合。
从杂货店的例子,我们可以得到如果存在一个频繁项集{diapers,wine},那么就可能有一条关联规则“diaperwine”,这意味着如果有人购买了diapers,那么他购买wine的概率会很大。但是反之并不一定成立。我们前面给出了关联规则置信度的量化定义。在这里一条规则
“diaperwine”的置信度定义为support(diaper,wine)/support(diapers)。所以现在看来,要想求关联规则,就先获取置信度,而置信度通过公式就是将前面求得的支持度做了个除法。
那么从一个频繁项集中,能够生成多少规则呢?我们假设频繁项集{0,1,2,3}那么下图给出了关联规则网格示意图。
grid
与频繁项集的生成类似,我们也能找到一个原理来减少规则数目从而降低计算量。图中阴影部分给出了低置信度的规则,可以观察得到,如果某条规则并不满足最小置信度要求,那么该规则的所有子集也不会满足最小置信度要求。与前面讲述的apriori算法类似,首先从一个频繁项集开始,接着创建一个规则列表,其中规则右部只包含元素,然后对这些规则进行测试。接下来合并所有剩余规则来创建一个新的规则列表,其中规则右部包含两个元素。这种方法也被称作分级法。
下面我们直接给出该算法的实现代码:

def generateRules(L, supportData, minConf=0.7):  #supportData is a dict coming from scanD
    bigRuleList = []
    for i in range(1, len(L)):#only get the sets with two or more items
        for freqSet in L[i]:
            H1 = [frozenset([item]) for item in freqSet]
            if (i > 1):
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
            else:
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList         

def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    prunedH = [] #create new list to return
    for conseq in H:
        conf = supportData[freqSet]/supportData[freqSet-conseq] #calc confidence
        if conf >= minConf: 
            print(freqSet-conseq,'-->',conseq,'conf:',conf)
            brl.append((freqSet-conseq, conseq, conf))
            prunedH.append(conseq)
    return prunedH

def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
    m = len(H[0])
    if (len(freqSet) > (m + 1)): #try further merging
        Hmp1 = aprioriGen(H, m+1)#create Hm+1 new candidates
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        if (len(Hmp1) > 1):    #need at least two sets to merge
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)

上述代码一共有三个函数,其中第一个函数generateRules()是主函数,其余两个函数rulesFromConseq()和calcConf分别用于生成候选规则集合以及对规则进行评估。
看一下实际运行效果:

##association
##生成一个最小支持度为0.5的频繁项的集合
L,suppData = apriori.apriori(dataSet,minSupport=0.5)
rules =apriori.generateRules(L,suppData,minConf=0.7)
rules
frozenset({5}) --> frozenset({2}) conf: 1.0
frozenset({2}) --> frozenset({5}) conf: 1.0
frozenset({1}) --> frozenset({3}) conf: 1.0

[(frozenset({5}), frozenset({2}), 1.0),
 (frozenset({2}), frozenset({5}), 1.0),
 (frozenset({1}), frozenset({3}), 1.0)]

总结

关联分析是用于发现大数据集中元素间有趣关系的一个工具集,可以采用两种方式来量化这些有趣的关系。第一种是频繁项集,第二种是关联规则。发现元素项间不同的组合是个十分耗时的任务,这就需要一些更智能的方法,比如Apriori算法来降低时间复杂度。但是这种算法还是存在问题的,因为在每次增加频繁项集的大小,Apriori都会重新扫描整个数据集。当数据集很大时,这回显著降低频繁项集发现的速度。FP-growth算法就能很好的解决这个问题。

作者:u010665216 发表于2017/11/8 14:03:41 原文链接
阅读:3 评论:0 查看评论

BZOJ1179 [Apio2009]Atm

$
0
0

标签:SPFA,tarjan缩点

Description


Input

第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号。接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。接下来一行包含两个整数S、P,S表示市中心的编号,也就是出发的路口。P表示酒吧数目。接下来的一行中有P个整数,表示P个有酒吧的路口的编号

Output

输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。

Sample Input

6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1 5
1 4
4
3
5
6

Sample Output

47

HINT

50%的输入保证N,M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。

 

算法很简单

Tarjan缩点重构图后跑一遍SPFA最长路就可以了

但是代码实现很复杂,很多细节,容易写挂题

SPFA中的初始点S,一定要赋值为belong[s]

剩下最短路中的,因为重构图,所以可以直接指向节点本身

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define dep(i,a,b) for(register int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxm=5e5+6,maxn=5e5+6;
int inq[maxn],que[maxn<<1],dfn[maxn],c[maxn],low[maxn],dis[maxn],v[maxn],tim=0,cnt=0,tot=0,top=0;
int last[maxn],rlast[maxn],belong[maxn];
int n,m,s,p;
struct edge{int to,next;}e[maxm<<1],re[maxm<<1];
#define reg(x) for(int i=last[x];i;i=e[i].next)
#define ov e[i].to
#define newreg(x) for(int i=rlast[x];i;i=re[i].next)
#define rv re[i].to

void tarjan(int x)
{
	int now=0;
	dfn[x]=low[x]=++tim;
	que[++top]=x;inq[x]=1;
	reg(x)
	    if(!dfn[ov]){tarjan(ov);low[x]=min(low[x],low[ov]);}
	    else if(inq[ov])low[x]=min(low[x],dfn[ov]);
	if(low[x]==dfn[x]){
		tot++;
		while(now!=x){
			now=que[top--];
			belong[now]=tot;
			v[tot]+=c[now];
			inq[now]=0;
		}
	}
}
void rebuild()
{
	cnt=0;
	rep(k,1,n)
	    reg(k)
	        if(belong[k]!=belong[ov]){
			    re[++cnt]=(edge){belong[k],rlast[belong[ov]]};rlast[belong[ov]]=cnt;
				//re[++cnt]=(edge){belong[ov],rlast[belong[k]]};rlast[belong[k]]=cnt;
			}
}

void spfa()
{
	int head=1,tail=1;
	que[head]=belong[s];inq[belong[s]]=1;dis[belong[s]]=v[belong[s]];
	while(head<=tail){
		int now=que[head++];
		newreg(now)
			if(dis[now]+v[rv]>dis[rv]){
				dis[rv]=dis[now]+v[rv];
				if(!inq[rv]){
					inq[rv]=1;
					que[++tail]=rv;
				}
			}
		inq[now]=0;
	}
}
	
			
int main()
{
	n=read(),m=read();
	rep(i,1,m){
		int x=read(),y=read();
		e[++cnt]=(edge){x,last[y]};last[y]=cnt;
		//e[++cnt]=(edge){y,last[x]};last[x]=cnt;
	}
	rep(i,1,n)c[i]=read();
	rep(i,1,n)if(!dfn[i])tarjan(i);
	s=read(),p=read();
	rebuild();
	spfa();
	int ans=0;
	rep(i,1,p){
		int x=read();
		ans=max(ans,dis[belong[x]]);
	}
	cout<<ans<<endl;
	return 0;
}


作者:qwerty1125 发表于2017/11/8 14:21:25 原文链接
阅读:8 评论:0 查看评论

Go语言学习之expvar包(公共变量)(the way to go)

$
0
0

生命不止,继续 go go go!!!

基础还是要打好,很久没有分享golang的标准包了,今天就来一个expvar包。

Package expvar

概述
Package expvar provides a standardized interface to public variables, such as operation counters in servers. It exposes these variables via HTTP at /debug/vars in JSON format.
Operations to set or modify these public variables are atomic.

In addition to adding the HTTP handler, this package registers the following variables:

cmdline   os.Args
memstats  runtime.Memstats

The package is sometimes only imported for the side effect of registering its HTTP handler and the above variables. To use it this way, link this package into your program:

import _ "expvar"

expvar包提供了公共变量的标准接口,如服务的操作计数器。本包通过HTTP在/debug/vars位置以JSON格式导出了这些变量。+
对这些公共变量的读写操作都是原子级的。
为了增加HTTP处理器,本包注册了如下变量:+

cmdline   os.Args
memstats  runtime.Memstats

有时候本包被导入只是为了获得本包注册HTTP处理器和上述变量的副作用。此时可以如下方式导入本包:

import _ "expvar"

支持类型
支持一些常见的类型:float64、int64、Map、String。
如果我们的程序要用到上面提的四种类型(其中,Map 类型要求 Key 是字符串)。可以考虑使用这个包。

功能
支持对变量的基本操作,修改、查询

整形类型,可以用来做计数器

线程安全的

此外还提供了调试接口,/debug/vars。它能够展示所有通过这个包创建的变量

所有的变量都是Var类型,可以自己通过实现这个接口扩展其它的类型

Handler()方法可以得到调试接口的http.Handler,和自己的路由对接

func Do

func Do(f func(KeyValue))

Do calls f for each exported variable. The global variable map is locked during the iteration, but existing entries may be concurrently updated.
Do对映射的每一条记录都调用f。迭代执行时会锁定该映射,但已存在的记录可以同时更新。

func Handler

func Handler() http.Handler

Handler returns the expvar HTTP Handler.

This is only needed to install the handler in a non-standard location.

func Publish

func Publish(name string, v Var)

Publish declares a named exported variable. This should be called from a package’s init function when it creates its Vars. If the name is already registered then this will log.Panic.
Publish声明一个导出变量。必须在init函数里调用。如果name已经被注册,会调用log.Panic。

源码
由于expvar的源码篇幅不是很大,就贴上了:

package expvar

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "math"
    "net/http"
    "os"
    "runtime"
    "sort"
    "strconv"
    "sync"
    "sync/atomic"
)

// Var is an abstract type for all exported variables.
type Var interface {
    // String returns a valid JSON value for the variable.
    // Types with String methods that do not return valid JSON
    // (such as time.Time) must not be used as a Var.
    String() string
}

// Int is a 64-bit integer variable that satisfies the Var interface.
type Int struct {
    i int64
}

func (v *Int) Value() int64 {
    return atomic.LoadInt64(&v.i)
}

func (v *Int) String() string {
    return strconv.FormatInt(atomic.LoadInt64(&v.i), 10)
}

func (v *Int) Add(delta int64) {
    atomic.AddInt64(&v.i, delta)
}

func (v *Int) Set(value int64) {
    atomic.StoreInt64(&v.i, value)
}

// Float is a 64-bit float variable that satisfies the Var interface.
type Float struct {
    f uint64
}

func (v *Float) Value() float64 {
    return math.Float64frombits(atomic.LoadUint64(&v.f))
}

func (v *Float) String() string {
    return strconv.FormatFloat(
        math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64)
}

// Add adds delta to v.
func (v *Float) Add(delta float64) {
    for {
        cur := atomic.LoadUint64(&v.f)
        curVal := math.Float64frombits(cur)
        nxtVal := curVal + delta
        nxt := math.Float64bits(nxtVal)
        if atomic.CompareAndSwapUint64(&v.f, cur, nxt) {
            return
        }
    }
}

// Set sets v to value.
func (v *Float) Set(value float64) {
    atomic.StoreUint64(&v.f, math.Float64bits(value))
}

// Map is a string-to-Var map variable that satisfies the Var interface.
type Map struct {
    m      sync.Map // map[string]Var
    keysMu sync.RWMutex
    keys   []string // sorted
}

// KeyValue represents a single entry in a Map.
type KeyValue struct {
    Key   string
    Value Var
}

func (v *Map) String() string {
    var b bytes.Buffer
    fmt.Fprintf(&b, "{")
    first := true
    v.Do(func(kv KeyValue) {
        if !first {
            fmt.Fprintf(&b, ", ")
        }
        fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value)
        first = false
    })
    fmt.Fprintf(&b, "}")
    return b.String()
}

// Init removes all keys from the map.
func (v *Map) Init() *Map {
    v.keysMu.Lock()
    defer v.keysMu.Unlock()
    v.keys = v.keys[:0]
    v.m.Range(func(k, _ interface{}) bool {
        v.m.Delete(k)
        return true
    })
    return v
}

// updateKeys updates the sorted list of keys in v.keys.
func (v *Map) addKey(key string) {
    v.keysMu.Lock()
    defer v.keysMu.Unlock()
    v.keys = append(v.keys, key)
    sort.Strings(v.keys)
}

func (v *Map) Get(key string) Var {
    i, _ := v.m.Load(key)
    av, _ := i.(Var)
    return av
}

func (v *Map) Set(key string, av Var) {
    // Before we store the value, check to see whether the key is new. Try a Load
    // before LoadOrStore: LoadOrStore causes the key interface to escape even on
    // the Load path.
    if _, ok := v.m.Load(key); !ok {
        if _, dup := v.m.LoadOrStore(key, av); !dup {
            v.addKey(key)
            return
        }
    }

    v.m.Store(key, av)
}

// Add adds delta to the *Int value stored under the given map key.
func (v *Map) Add(key string, delta int64) {
    i, ok := v.m.Load(key)
    if !ok {
        var dup bool
        i, dup = v.m.LoadOrStore(key, new(Int))
        if !dup {
            v.addKey(key)
        }
    }

    // Add to Int; ignore otherwise.
    if iv, ok := i.(*Int); ok {
        iv.Add(delta)
    }
}

// AddFloat adds delta to the *Float value stored under the given map key.
func (v *Map) AddFloat(key string, delta float64) {
    i, ok := v.m.Load(key)
    if !ok {
        var dup bool
        i, dup = v.m.LoadOrStore(key, new(Float))
        if !dup {
            v.addKey(key)
        }
    }

    // Add to Float; ignore otherwise.
    if iv, ok := i.(*Float); ok {
        iv.Add(delta)
    }
}

// Do calls f for each entry in the map.
// The map is locked during the iteration,
// but existing entries may be concurrently updated.
func (v *Map) Do(f func(KeyValue)) {
    v.keysMu.RLock()
    defer v.keysMu.RUnlock()
    for _, k := range v.keys {
        i, _ := v.m.Load(k)
        f(KeyValue{k, i.(Var)})
    }
}

// String is a string variable, and satisfies the Var interface.
type String struct {
    s atomic.Value // string
}

func (v *String) Value() string {
    p, _ := v.s.Load().(string)
    return p
}

// String implements the Val interface. To get the unquoted string
// use Value.
func (v *String) String() string {
    s := v.Value()
    b, _ := json.Marshal(s)
    return string(b)
}

func (v *String) Set(value string) {
    v.s.Store(value)
}

// Func implements Var by calling the function
// and formatting the returned value using JSON.
type Func func() interface{}

func (f Func) Value() interface{} {
    return f()
}

func (f Func) String() string {
    v, _ := json.Marshal(f())
    return string(v)
}

// All published variables.
var (
    vars      sync.Map // map[string]Var
    varKeysMu sync.RWMutex
    varKeys   []string // sorted
)

// Publish declares a named exported variable. This should be called from a
// package's init function when it creates its Vars. If the name is already
// registered then this will log.Panic.
func Publish(name string, v Var) {
    if _, dup := vars.LoadOrStore(name, v); dup {
        log.Panicln("Reuse of exported var name:", name)
    }
    varKeysMu.Lock()
    defer varKeysMu.Unlock()
    varKeys = append(varKeys, name)
    sort.Strings(varKeys)
}

// Get retrieves a named exported variable. It returns nil if the name has
// not been registered.
func Get(name string) Var {
    i, _ := vars.Load(name)
    v, _ := i.(Var)
    return v
}

// Convenience functions for creating new exported variables.

func NewInt(name string) *Int {
    v := new(Int)
    Publish(name, v)
    return v
}

func NewFloat(name string) *Float {
    v := new(Float)
    Publish(name, v)
    return v
}

func NewMap(name string) *Map {
    v := new(Map).Init()
    Publish(name, v)
    return v
}

func NewString(name string) *String {
    v := new(String)
    Publish(name, v)
    return v
}

// Do calls f for each exported variable.
// The global variable map is locked during the iteration,
// but existing entries may be concurrently updated.
func Do(f func(KeyValue)) {
    varKeysMu.RLock()
    defer varKeysMu.RUnlock()
    for _, k := range varKeys {
        val, _ := vars.Load(k)
        f(KeyValue{k, val.(Var)})
    }
}

func expvarHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    fmt.Fprintf(w, "{\n")
    first := true
    Do(func(kv KeyValue) {
        if !first {
            fmt.Fprintf(w, ",\n")
        }
        first = false
        fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
    })
    fmt.Fprintf(w, "\n}\n")
}

// Handler returns the expvar HTTP Handler.
//
// This is only needed to install the handler in a non-standard location.
func Handler() http.Handler {
    return http.HandlerFunc(expvarHandler)
}

func cmdline() interface{} {
    return os.Args
}

func memstats() interface{} {
    stats := new(runtime.MemStats)
    runtime.ReadMemStats(stats)
    return *stats
}

func init() {
    http.HandleFunc("/debug/vars", expvarHandler)
    Publish("cmdline", Func(cmdline))
    Publish("memstats", Func(memstats))
}

简单应用

例1
main.go

package main

import (
    "expvar"
    "net"
    "net/http"
)

var (
    test = expvar.NewMap("Test")
)

func init() {
    test.Add("go", 10)
    test.Add("go1", 10)
}

func main() {
    sock, err := net.Listen("tcp", "localhost:8080")
    if err != nil {
        panic("error")
    }
    go func() {
        http.Serve(sock, nil)
    }()
    select {}
}

浏览器访问:http://localhost:8080/debug/vars
这里写图片描述

例2
main.go

package main

import (
    "expvar"
    "io"
    "net/http"
    "strconv"
    "time"

    "github.com/paulbellamy/ratecounter"
)

var (
    counter       *ratecounter.RateCounter
    hitsperminute = expvar.NewInt("hits_per_minute")
)

func increment(w http.ResponseWriter, r *http.Request) {
    counter.Incr(1)
    hitsperminute.Set(counter.Rate())
    io.WriteString(w, strconv.FormatInt(counter.Rate(), 10))
}

func main() {
    counter = ratecounter.NewRateCounter(1 * time.Minute)
    http.HandleFunc("/increment", increment)
    http.ListenAndServe(":8000", nil)
}

浏览器输入:http://localhost:8000/increment
curl命令行访问: curl http://localhost:8000/increment

浏览器访问:http://localhost:8000/debug/vars
这里写图片描述

完美例子

https://golang.org/src/net/http/triv.go?m=text

延伸

https://github.com/divan/expvarmon
之后会有详细介绍

作者:wangshubo1989 发表于2017/11/8 15:22:04 原文链接
阅读:5 评论:0 查看评论

Advanced Programming in UNIX Environment Episode 13

$
0
0

文件共享
内核使用3中数据结构表示打开文件,他们之间的关系决定了文件共享方面的一个进程对另一个进程可能产生的影响。
(1)每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述相关联的是:
a. 文件描述符标识(close_on_exec);
b. 指向一个文件表项的指针。
(2)内核为所有打开文件维持一张文件表。每个文件表项包含:
a. 文件状态标志(读、写、添写、同步和非阻塞等);
b. 当前文件偏移量;
c. 指向文件v节点表项的指针。
(3)每个打开文件(或设备)都有一个v节点(v-node)结构。

Linux没有使用v节点,而是使用了通用i节点结构。虽然两种实现有所不同,但在概念上v节点与i节点是一样的。两者都指向文件系统特有的i节点结构。

假定第一个进程在文件描述符3上打开该文件,而另一个进程在文件描述符4上打开该文件。

  • 在完成每个write后,在文件表项中的当前文件偏移量即增加所写入的字节数。如果这导致当前文件偏移量超出了当前文件长度,则将i节点表项中的当前文件长度设置为当前文件偏移量(也就时是该文件加长了)。
  • 如果使用O_APPEND标志打开一个文件,则相应标志也被设置到文件表项的文件状态中。每次对这种具有追加写标志的文件执行写操作时,文件表项中的当前文件偏移量首先会被设置为i节点表项中的文件长度。这就使得每次写入的数据都追加到文件的当前尾端处。
  • 若一个文件用lseek定位到文件当前的尾端,则文件表项中的当前文件偏移量被设置为i节点表项中的当前文件长度(注意,这与用O_APPEND标志打开文件是不同的。)。
  • lseek函数只修改文件表项中的当前文件偏移量,不进行任何I/O操作。
作者:myfather103 发表于2017/11/8 15:25:44 原文链接
阅读:3 评论:0 查看评论

洛谷P1962 斐波那契数列

$
0
0

矩阵乘法

题目传送门

求斐波那契数列第n项谁都会,但是这里n有long long ,O(n)推显然是不行的。于是我们就需要考虑使用矩阵快速幂优化。

构造一个矩阵[ f[n1]f[n] ]
再构造一个变换矩阵,使得[ f[n2]f[n1] ]×x=[ f[n1]f[n] ]

很显然x是2*2的矩阵。不妨设x=[acbd]

那么有如下等式:

[ f[n2]f[n1] ]×[acbd]=[ f[n1]f[n] ]

根据矩阵乘法,可以得到各项系数:
x=[0111]

根据矩乘的结合率,我们可以得到:

[ f[1]f[2] ]×[0111]n2=[ f[n1]f[n] ]

于是我们就可以用快速幂在O(log2n)的时间得到第n项了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
struct Matrix{
    int n,m;
    LL a[3][3];
}a,b;
LL n;
Matrix operator * (const Matrix &x,const Matrix &b){
    Matrix c;
    c.n=x.n,c.m=b.m;
    for (int i=1;i<=c.n;i++)
        for (int j=1;j<=c.m;j++){
            c.a[i][j]=0;
            for (int k=1;k<=x.m;k++)
                (c.a[i][j]+=x.a[i][k]*b.a[k][j])%=MOD;
        }
    return c;
} 
Matrix ksm(Matrix a,LL b){
    Matrix ret;
    ret.m=ret.n=2;
    for (int i=1;i<=ret.n;i++)
        for (int j=1;j<=ret.m;j++)
            ret.a[i][j]=1;
    while (b){
        if (b&1) ret=ret*a;
        b>>=1; a=a*a;
    }
    return ret;
}
int main(){
    scanf("%lld",&n);
    if (n==1) return printf("1\n"),0; 
    b.a[1][1]=0; b.a[1][2]=1;
    b.a[2][1]=1; b.a[2][2]=1;
    b.n=b.m=2;
    a.a[1][1]=0; a.a[1][2]=1;
    a.n=1,a.m=2;
    printf("%lld\n",(a*ksm(b,n-2)).a[1][2]);
    return 0;
} 
作者:a1799342217 发表于2017/11/8 15:44:26 原文链接
阅读:2 评论:0 查看评论

最简单的基于FFmpeg的AVDevice例子(屏幕录制)

$
0
0

原文地址:http://blog.csdn.net/leixiaohua1020/article/details/39706721 雷神的博客,致敬雷神。


抓屏方法

上篇文章记录了libavdevice的使用方法,本文不再重复。在Windows系统使用libavdevice抓取屏幕数据有两种方法:gdigrab和dshow。下文分别介绍。
1. gdigrab
gdigrab是FFmpeg专门用于抓取Windows桌面的设备。非常适合用于屏幕录制。它通过不同的输入URL支持两种方式的抓取:
(1)“desktop”:抓取整张桌面。或者抓取桌面中的一个特定的区域。
(2)“title={窗口名称}”:抓取屏幕中特定的一个窗口(目前中文窗口还有乱码问题)。
gdigrab另外还支持一些参数,用于设定抓屏的位置:
offset_x:抓屏起始点横坐标。
offset_y:抓屏起始点纵坐标。
video_size:抓屏的大小。
framerate:抓屏的帧率。
参考的代码如下:

  1. //Use gdigrab  
  2.  AVDictionary* options = NULL;  
  3.  //Set some options  
  4.  //grabbing frame rate  
  5.  //av_dict_set(&options,"framerate","5",0);  
  6.  //The distance from the left edge of the screen or desktop  
  7.  //av_dict_set(&options,"offset_x","20",0);  
  8.  //The distance from the top edge of the screen or desktop  
  9.  //av_dict_set(&options,"offset_y","40",0);  
  10.  //Video frame size. The default is to capture the full screen  
  11.  //av_dict_set(&options,"video_size","640x480",0);  
  12.  AVInputFormat *ifmt=av_find_input_format("gdigrab");  
  13.  if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){  
  14.   printf("Couldn't open input stream.(无法打开输入流)\n");  
  15.   return -1;  
  16.   }  

2. dshow
使用dshow抓屏需要安装抓屏软件:screen-capture-recorder
软件地址:http://sourceforge.net/projects/screencapturer/
下载软件安装完成后,可以指定dshow的输入设备为“screen-capture-recorder”即可。有关dshow设备的使用方法在上一篇文章中已经有详细叙述,这里不再重复。参考的代码如下:
  1. AVInputFormat *ifmt=av_find_input_format("dshow");  
  2.  if(avformat_open_input(&pFormatCtx,"video=screen-capture-recorder",ifmt,NULL)!=0){  
  3.   printf("Couldn't open input stream.(无法打开输入流)\n");  
  4.   return -1;  
  5.  }  

注:上述两种抓屏方法也可以直接使用ffmpeg.exe的命令行完成,可以参考文章:

FFmpeg获取DirectShow设备数据(摄像头,录屏)

在Linux下可以使用x11grab抓屏,在MacOS下可以使用avfoundation抓屏,在这里不再详细叙述。

代码

下面直接贴上程序代码:

  1. /** 
  2.  * 最简单的基于FFmpeg的AVDevice例子(屏幕录制) 
  3.  * Simplest FFmpeg Device (Screen Capture) 
  4.  * 
  5.  * 雷霄骅 Lei Xiaohua 
  6.  * leixiaohua1020@126.com 
  7.  * 中国传媒大学/数字电视技术 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 
  11.  * 本程序实现了屏幕录制功能。可以录制并播放桌面数据。是基于FFmpeg 
  12.  * 的libavdevice类库最简单的例子。通过该例子,可以学习FFmpeg中 
  13.  * libavdevice类库的使用方法。 
  14.  * 本程序在Windows下可以使用2种方式录制屏幕: 
  15.  *  1.gdigrab: Win32下的基于GDI的屏幕录制设备。 
  16.  *             抓取桌面的时候,输入URL为“desktop”。 
  17.  *  2.dshow: 使用Directshow。注意需要安装额外的软件screen-capture-recorder 
  18.  * 在Linux下可以使用x11grab录制屏幕。 
  19.  * 在MacOS下可以使用avfoundation录制屏幕。 
  20.  * 
  21.  * This software capture screen of computer. It's the simplest example 
  22.  * about usage of FFmpeg's libavdevice Library.  
  23.  * It's suiltable for the beginner of FFmpeg. 
  24.  * This software support 2 methods to capture screen in Microsoft Windows: 
  25.  *  1.gdigrab: Win32 GDI-based screen capture device. 
  26.  *             Input URL in avformat_open_input() is "desktop". 
  27.  *  2.dshow: Use Directshow. Need to install screen-capture-recorder. 
  28.  * It use x11grab to capture screen in Linux. 
  29.  * It use avfoundation to capture screen in MacOS. 
  30.  */  
  31.   
  32.   
  33. #include <stdio.h>  
  34.   
  35. #define __STDC_CONSTANT_MACROS  
  36.   
  37. #ifdef _WIN32  
  38. //Windows  
  39. extern "C"  
  40. {  
  41. #include "libavcodec/avcodec.h"  
  42. #include "libavformat/avformat.h"  
  43. #include "libswscale/swscale.h"  
  44. #include "libavdevice/avdevice.h"  
  45. #include "SDL/SDL.h"  
  46. };  
  47. #else  
  48. //Linux...  
  49. #ifdef __cplusplus  
  50. extern "C"  
  51. {  
  52. #endif  
  53. #include <libavcodec/avcodec.h>  
  54. #include <libavformat/avformat.h>  
  55. #include <libswscale/swscale.h>  
  56. #include <libavdevice/avdevice.h>  
  57. #include <SDL/SDL.h>  
  58. #ifdef __cplusplus  
  59. };  
  60. #endif  
  61. #endif  
  62.   
  63. //Output YUV420P   
  64. #define OUTPUT_YUV420P 0  
  65. //'1' Use Dshow   
  66. //'0' Use GDIgrab  
  67. #define USE_DSHOW 0  
  68.   
  69. //Refresh Event  
  70. #define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)  
  71.   
  72. #define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)  
  73.   
  74. int thread_exit=0;  
  75.   
  76. int sfp_refresh_thread(void *opaque)  
  77. {  
  78.     thread_exit=0;  
  79.     while (!thread_exit) {  
  80.         SDL_Event event;  
  81.         event.type = SFM_REFRESH_EVENT;  
  82.         SDL_PushEvent(&event);  
  83.         SDL_Delay(40);  
  84.     }  
  85.     thread_exit=0;  
  86.     //Break  
  87.     SDL_Event event;  
  88.     event.type = SFM_BREAK_EVENT;  
  89.     SDL_PushEvent(&event);  
  90.   
  91.     return 0;  
  92. }  
  93.   
  94. //Show Dshow Device  
  95. void show_dshow_device(){  
  96.     AVFormatContext *pFormatCtx = avformat_alloc_context();  
  97.     AVDictionary* options = NULL;  
  98.     av_dict_set(&options,"list_devices","true",0);  
  99.     AVInputFormat *iformat = av_find_input_format("dshow");  
  100.     printf("========Device Info=============\n");  
  101.     avformat_open_input(&pFormatCtx,"video=dummy",iformat,&options);  
  102.     printf("================================\n");  
  103. }  
  104.   
  105. //Show AVFoundation Device  
  106. void show_avfoundation_device(){  
  107.     AVFormatContext *pFormatCtx = avformat_alloc_context();  
  108.     AVDictionary* options = NULL;  
  109.     av_dict_set(&options,"list_devices","true",0);  
  110.     AVInputFormat *iformat = av_find_input_format("avfoundation");  
  111.     printf("==AVFoundation Device Info===\n");  
  112.     avformat_open_input(&pFormatCtx,"",iformat,&options);  
  113.     printf("=============================\n");  
  114. }  
  115.   
  116.   
  117.   
  118. int main(int argc, char* argv[])  
  119. {  
  120.   
  121.     AVFormatContext *pFormatCtx;  
  122.     int             i, videoindex;  
  123.     AVCodecContext  *pCodecCtx;  
  124.     AVCodec         *pCodec;  
  125.       
  126.     av_register_all();  
  127.     avformat_network_init();  
  128.     pFormatCtx = avformat_alloc_context();  
  129.       
  130.     //Open File  
  131.     //char filepath[]="src01_480x272_22.h265";  
  132.     //avformat_open_input(&pFormatCtx,filepath,NULL,NULL)  
  133.   
  134.     //Register Device  
  135.     avdevice_register_all();  
  136.     //Windows  
  137. #ifdef _WIN32  
  138. #if USE_DSHOW  
  139.     //Use dshow  
  140.     //  
  141.     //Need to Install screen-capture-recorder  
  142.     //screen-capture-recorder  
  143.     //Website: http://sourceforge.net/projects/screencapturer/  
  144.     //  
  145.     AVInputFormat *ifmt=av_find_input_format("dshow");  
  146.     if(avformat_open_input(&pFormatCtx,"video=screen-capture-recorder",ifmt,NULL)!=0){  
  147.         printf("Couldn't open input stream.\n");  
  148.         return -1;  
  149.     }  
  150. #else  
  151.     //Use gdigrab  
  152.     AVDictionary* options = NULL;  
  153.     //Set some options  
  154.     //grabbing frame rate  
  155.     //av_dict_set(&options,"framerate","5",0);  
  156.     //The distance from the left edge of the screen or desktop  
  157.     //av_dict_set(&options,"offset_x","20",0);  
  158.     //The distance from the top edge of the screen or desktop  
  159.     //av_dict_set(&options,"offset_y","40",0);  
  160.     //Video frame size. The default is to capture the full screen  
  161.     //av_dict_set(&options,"video_size","640x480",0);  
  162.     AVInputFormat *ifmt=av_find_input_format("gdigrab");  
  163.     if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){  
  164.         printf("Couldn't open input stream.\n");  
  165.         return -1;  
  166.     }  
  167.   
  168. #endif  
  169. #elif defined linux  
  170.     //Linux  
  171.     AVDictionary* options = NULL;  
  172.     //Set some options  
  173.     //grabbing frame rate  
  174.     //av_dict_set(&options,"framerate","5",0);  
  175.     //Make the grabbed area follow the mouse  
  176.     //av_dict_set(&options,"follow_mouse","centered",0);  
  177.     //Video frame size. The default is to capture the full screen  
  178.     //av_dict_set(&options,"video_size","640x480",0);  
  179.     AVInputFormat *ifmt=av_find_input_format("x11grab");  
  180.     //Grab at position 10,20  
  181.     if(avformat_open_input(&pFormatCtx,":0.0+10,20",ifmt,&options)!=0){  
  182.         printf("Couldn't open input stream.\n");  
  183.         return -1;  
  184.     }  
  185. #else  
  186.     show_avfoundation_device();  
  187.     //Mac  
  188.     AVInputFormat *ifmt=av_find_input_format("avfoundation");  
  189.     //Avfoundation  
  190.     //[video]:[audio]  
  191.     if(avformat_open_input(&pFormatCtx,"1",ifmt,NULL)!=0){  
  192.         printf("Couldn't open input stream.\n");  
  193.         return -1;  
  194.     }  
  195. #endif  
  196.   
  197.     if(avformat_find_stream_info(pFormatCtx,NULL)<0)  
  198.     {  
  199.         printf("Couldn't find stream information.\n");  
  200.         return -1;  
  201.     }  
  202.     videoindex=-1;  
  203.     for(i=0; i<pFormatCtx->nb_streams; i++)   
  204.         if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)  
  205.         {  
  206.             videoindex=i;  
  207.             break;  
  208.         }  
  209.     if(videoindex==-1)  
  210.     {  
  211.         printf("Didn't find a video stream.\n");  
  212.         return -1;  
  213.     }  
  214.     pCodecCtx=pFormatCtx->streams[videoindex]->codec;  
  215.     pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  
  216.     if(pCodec==NULL)  
  217.     {  
  218.         printf("Codec not found.\n");  
  219.         return -1;  
  220.     }  
  221.     if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)  
  222.     {  
  223.         printf("Could not open codec.\n");  
  224.         return -1;  
  225.     }  
  226.     AVFrame *pFrame,*pFrameYUV;  
  227.     pFrame=av_frame_alloc();  
  228.     pFrameYUV=av_frame_alloc();  
  229.     //unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));  
  230.     //avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);  
  231.     //SDL----------------------------  
  232.     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {    
  233.         printf( "Could not initialize SDL - %s\n", SDL_GetError());   
  234.         return -1;  
  235.     }   
  236.     int screen_w=640,screen_h=360;  
  237.     const SDL_VideoInfo *vi = SDL_GetVideoInfo();  
  238.     //Half of the Desktop's width and height.  
  239.     screen_w = vi->current_w/2;  
  240.     screen_h = vi->current_h/2;  
  241.     SDL_Surface *screen;   
  242.     screen = SDL_SetVideoMode(screen_w, screen_h, 0,0);  
  243.   
  244.     if(!screen) {    
  245.         printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError());    
  246.         return -1;  
  247.     }  
  248.     SDL_Overlay *bmp;   
  249.     bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen);   
  250.     SDL_Rect rect;  
  251.     rect.x = 0;      
  252.     rect.y = 0;      
  253.     rect.w = screen_w;      
  254.     rect.h = screen_h;    
  255.     //SDL End------------------------  
  256.     int ret, got_picture;  
  257.   
  258.     AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));  
  259.   
  260. #if OUTPUT_YUV420P   
  261.     FILE *fp_yuv=fopen("output.yuv","wb+");    
  262. #endif    
  263.   
  264.     struct SwsContext *img_convert_ctx;  
  265.     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);   
  266.     //------------------------------  
  267.     SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread,NULL);  
  268.     //  
  269.     SDL_WM_SetCaption("Simplest FFmpeg Grab Desktop",NULL);  
  270.     //Event Loop  
  271.     SDL_Event event;  
  272.   
  273.     for (;;) {  
  274.         //Wait  
  275.         SDL_WaitEvent(&event);  
  276.         if(event.type==SFM_REFRESH_EVENT){  
  277.             //------------------------------  
  278.             if(av_read_frame(pFormatCtx, packet)>=0){  
  279.                 if(packet->stream_index==videoindex){  
  280.                     ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);  
  281.                     if(ret < 0){  
  282.                         printf("Decode Error.\n");  
  283.                         return -1;  
  284.                     }  
  285.                     if(got_picture){  
  286.                         SDL_LockYUVOverlay(bmp);  
  287.                         pFrameYUV->data[0]=bmp->pixels[0];  
  288.                         pFrameYUV->data[1]=bmp->pixels[2];  
  289.                         pFrameYUV->data[2]=bmp->pixels[1];       
  290.                         pFrameYUV->linesize[0]=bmp->pitches[0];  
  291.                         pFrameYUV->linesize[1]=bmp->pitches[2];     
  292.                         pFrameYUV->linesize[2]=bmp->pitches[1];  
  293.                         sws_scale(img_convert_ctx, (const unsigned charconst*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);  
  294.   
  295. #if OUTPUT_YUV420P    
  296.                         int y_size=pCodecCtx->width*pCodecCtx->height;      
  297.                         fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y     
  298.                         fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U    
  299.                         fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V    
  300. #endif    
  301.                         SDL_UnlockYUVOverlay(bmp);   
  302.                           
  303.                         SDL_DisplayYUVOverlay(bmp, &rect);   
  304.   
  305.                     }  
  306.                 }  
  307.                 av_free_packet(packet);  
  308.             }else{  
  309.                 //Exit Thread  
  310.                 thread_exit=1;  
  311.             }  
  312.         }else if(event.type==SDL_QUIT){  
  313.             thread_exit=1;  
  314.         }else if(event.type==SFM_BREAK_EVENT){  
  315.             break;  
  316.         }  
  317.   
  318.     }  
  319.   
  320.   
  321.     sws_freeContext(img_convert_ctx);  
  322.   
  323. #if OUTPUT_YUV420P   
  324.     fclose(fp_yuv);  
  325. #endif   
  326.   
  327.     SDL_Quit();  
  328.   
  329.     //av_free(out_buffer);  
  330.     av_free(pFrameYUV);  
  331.     avcodec_close(pCodecCtx);  
  332.     avformat_close_input(&pFormatCtx);  
  333.   
  334.     return 0;  
  335. }  





结果

程序的运行效果如下。这个运行结果还是十分有趣的,会出现一个屏幕“嵌套”在另一个屏幕里面的现象,环环相套。
 
可以通过代码定义的宏来确定是否将解码后的YUV420P数据输出成文件:
  1. #define OUTPUT_YUV420P 0  

可以通过下面的宏定义来确定使用GDIGrab或者是Dshow打开摄像头:

  1. //'1' Use Dshow   
  2. //'0' Use GDIgrab  
  3. #define USE_DSHOW 0  


下载


Simplest FFmpeg Device 


项目主页

SourceForge:https://sourceforge.net/projects/simplestffmpegdevice/

Github:https://github.com/leixiaohua1020/simplest_ffmpeg_device

开源中国:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_device


CSDN下载地址:
http://download.csdn.net/detail/leixiaohua1020/7994049

注:
 本工程包含两个基于FFmpeg的libavdevice的例子:
 simplest_ffmpeg_grabdesktop:屏幕录制。
 simplest_ffmpeg_readcamera:读取摄像头


更新-1.1(2015.1.9)=========================================

该版本中,修改了SDL的显示方式,弹出的窗口可以移动了。

CSDN下载地址:http://download.csdn.net/detail/leixiaohua1020/8344695


更新-1.2 (2015.2.13)=========================================

这次考虑到了跨平台的要求,调整了源代码。经过这次调整之后,源代码可以在以下平台编译通过:

VC++:打开sln文件即可编译,无需配置。

cl.exe:打开compile_cl.bat即可命令行下使用cl.exe进行编译,注意可能需要按照VC的安装路径调整脚本里面的参数。编译命令如下。

[plain] view plain copy
  1. ::VS2010 Environment  
  2. call "D:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"  
  3. ::include  
  4. @set INCLUDE=include;%INCLUDE%  
  5. ::lib  
  6. @set LIB=lib;%LIB%  
  7. ::compile and link  
  8. cl simplest_ffmpeg_grabdesktop.cpp /MD /link SDL.lib SDLmain.lib avcodec.lib ^  
  9. avformat.lib avutil.lib avdevice.lib avfilter.lib postproc.lib swresample.lib swscale.lib ^  
  10. /SUBSYSTEM:WINDOWS /OPT:NOREF  

MinGW:MinGW命令行下运行compile_mingw.sh即可使用MinGW的g++进行编译。编译命令如下。

[plain] view plain copy
  1. g++ simplest_ffmpeg_grabdesktop.cpp -g -o simplest_ffmpeg_grabdesktop.exe \  
  2. -I /usr/local/include -L /usr/local/lib \  
  3. -lmingw32 -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lavdevice -lswscale  

GCC(Linux):Linux命令行下运行compile_gcc.sh即可使用GCC进行编译。编译命令如下。

[plain] view plain copy
  1. gcc simplest_ffmpeg_grabdesktop.cpp -g -o simplest_ffmpeg_grabdesktop.out \  
  2. -I /usr/local/include -L /usr/local/lib -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lavdevice -lswscale  

GCC(MacOS):MacOS命令行下运行compile_gcc_mac.sh即可使用GCC进行编译。Mac的GCC和Linux的GCC差别不大,但是使用SDL1.2的时候,必须加上“-framework Cocoa”参数,否则编译无法通过。编译命令如下。

[plain] view plain copy
  1. gcc simplest_ffmpeg_grabdesktop.cpp -g -o simplest_ffmpeg_grabdesktop.out \  
  2. -framework Cocoa -I /usr/local/include -L /usr/local/lib -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lavdevice -lswscale  

PS:相关的编译命令已经保存到了工程文件夹中


CSDN下载地址:http://download.csdn.net/detail/leixiaohua1020/8445747

SourceForge上已经更新。


如有错误请指正:

交流请加QQ群:62054820
QQ:379969650.




作者:zhuweigangzwg 发表于2017/11/8 15:51:10 原文链接
阅读:1 评论:0 查看评论

第5章 TCP客户/服务器程序示例

$
0
0

客户端:

#include "../Gnet.h"

void do_client(int connfd)
{
    char buf[MAX_LINE];

    while(fgets(buf, MAX_LINE, stdin) != NULL)
    {
        Write(connfd, buf, strlen(buf));
        memset(buf, 0, MAX_LINE);
        if(Read(connfd, buf, MAX_LINE) == 0)
            perr_exit("server terminated.\n");
        fputs(buf, stdout);
    }
}

int main(int argc, const char* argv[])
{
    int connfd;
    struct sockaddr_in server_addr;

    if(argc < 2)
        perr_exit("usage : client <IPaddress>");

    connfd = Socket(AF_INET, SOCK_STREAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
    Connect(connfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    do_client(connfd);

    return 0;
}

服务器:

#include "../Gnet.h"

void do_server(int connfd)
{
    ssize_t nread;
    char buf[MAX_LINE];

    while((nread = Read(connfd, buf, MAX_LINE)) > 0)
        Write(connfd, buf, nread);
}

void sig_child(int signo)
{
    pid_t pid;
    int stat;

    printf("in sig_child.\n");
    while((pid = waitpid(-1,&stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);
    printf("out sig_child.\n");
}

int main(int argc, const char* argv[])
{
    int lfd, connfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;
    pid_t child_id;

    struct sigaction sigaction_set, sigaction_get;
    sigaction_set.sa_handler = sig_child;
    sigemptyset(&sigaction_set.sa_mask);
    sigaction_set.sa_flags = 0;
    if(sigaction(SIGCHLD, &sigaction_set, &sigaction_get) <0)
        printf("sigaction(SIGCHLD) error!\n");

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    lfd = Socket(AF_INET, SOCK_STREAM, 0);
    Bind(lfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
    Listen(lfd, LISTENQ);
    printf("waiting for connecting.\n");

    memset(&client_addr, 0, sizeof(client_addr));
    while(1)
    {
        client_addr_len = sizeof(client_addr_len);
        connfd = Accept(lfd, (struct sockaddr*)&client_addr, &client_addr_len);

        if((child_id = fork()) == 0)//子进程
        {
            Close(lfd);
            do_server(connfd);
            Close(connfd);
            exit(0);
        }
        else//父进程
        {
            printf("child %d connected\n", child_id);
            Close(connfd);
        }
    }

    return 0;
}

对基本的网络函数做了封装:

Gnet.h

#ifndef __GNET_H__

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include <signal.h>
#include <wait.h>

#define SERVER_PORT (49152)
#define LISTENQ (5)
#define MAX_LINE (256)

void perr_exit(const char* str);
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);
int Close(int fd);

ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
//ssize_t my_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);

#endif

Gnet.c

#include "Gnet.h"

void perr_exit(const char* str)
{
    perror(str);
    exit(-1);
}

int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
   int afd;

AGAIN:
   if((afd = accept(fd, sa, salenptr)) <0 )
   {
       if((errno == ECONNABORTED) || (errno == EINTR))
           goto AGAIN;
       else
           perr_exit("accept error");
   }

   return afd;
}

int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
    int bfd;

    if((bfd = bind(fd, sa, salen)) < 0)
        perr_exit("bind error");

    return bfd;
}

int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{
    int cfd;

    if((cfd = connect(fd, sa, salen)) < 0)
        perr_exit("connect error");

    return cfd;
}

int Listen(int fd, int backlog)
{
    int res;

    if((res = listen(fd, backlog)) < 0)
        perr_exit("listen error");

    return res;
}

int Socket(int family, int type, int protocol)
{
    int sfd;

    if((sfd = socket(family, type, protocol)) < 0)
        perr_exit("socket error");

    return sfd;
}

ssize_t Read(int fd, void* ptr, size_t nbytes)
{
    ssize_t n;

AGAIN:
    if((n = read(fd, ptr, nbytes)) == -1)
    {
        if(errno == EINTR)
            goto AGAIN;
        else
            return -1;
    }

    return n;
}

ssize_t Write(int fd, const void* ptr, size_t nbytes)
{
    ssize_t n;

AGAIN:
    if((n = write(fd, ptr, nbytes)) == -1)
    {
        if(errno == EINTR)
            goto AGAIN;
        else
            return -1;
    }

    return n;
}

int Close(int fd)
{
    int res;

    if((res = close(fd)) == -1)
        perr_exit("close error");

    return res;
}

ssize_t Readn(int fd, void* vptr, size_t n)
{
    ssize_t nleft;
    ssize_t nread;
    char* ptr;

    ptr = (char*)vptr;
    nleft = n;

    while(nleft > 0)
    {
        if((nread = read(fd, ptr, nleft)) < 0)
        {
            if(errno == EINTR)
                nread = 0;
            else
                return -1;
        }
        else if(nread == 0)
            break;

        nleft -= nread;
        ptr += nread;
    }       

    return n-nleft;
}

ssize_t Writen(int fd, const void* vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char* ptr;

    ptr = (const char*)vptr;
    nleft = n;

    while(nleft > 0)
    {
        if((nwritten = write(fd, ptr, nleft)) <= 0)
        {
            if(nwritten < 0 && errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }

        nleft -= nwritten;
        ptr += nwritten;
    }

    return n - nleft;
}

static ssize_t my_read(int fd, char* ptr)
{
    static int read_cnt;
    static char* read_ptr;
    static char read_buf[MAX_LINE];

    if(read_cnt <= 0)
    {
AGAIN:
        if((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)
        {
            if(errno == EINTR)
                goto AGAIN;
            else
                return -1;
        }
        else if(read_cnt == 0)
            return 0;

        read_ptr = read_buf;
    }

    --read_cnt;
    *ptr = *read_ptr++;

    return 1;
}

ssize_t Readline(int fd, void* vptr, size_t maxlen)
{
    ssize_t n, rc;
    char c, *ptr;

    ptr = (char*)vptr;

    for(n = 1; (size_t)n < maxlen; ++n)
    {
        if((rc = my_read(fd, &c)) == 1)
        {
            *ptr++ = c;
            if(c == '\n')
                break;
        }
        else if(rc == 0)
        {
            *ptr = '\0';
            return n-1;
        }
        else
            return -1;
    }

    *ptr = '\0';

    return n;
}

从今以后的学习代码都会上传到github:https://github.com/gongluck/unp-notes.git
作者:gongluck93 发表于2017/11/8 15:54:32 原文链接
阅读:0 评论:0 查看评论

【蓝桥杯】【密码发生器】

$
0
0

题目
在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:如果为了好记用生日吧,容易被破解,不安全;
如果设置不好记的密码,又担心自己也会忘记;如果写在纸上,担心纸张被别人发现或弄丢了…
这个程序的任务就是把一串拼音字母转换为6位数字(密码)。
我们可以使用任何好记的拼音串(比如名字,王喜明,就写:wangximing)作为输入,程序输出6位数字。
变换的过程如下:
第一步. 把字符串6个一组折叠起来,比如wangximing则变为:
wangxi
ming
第二步. 把所有垂直在同一个位置的字符的ascii码值相加,得出6个数字,如上面的例子,则得出:
228 202 220 206 120 105
第三步. 再把每个数字“缩位”处理:就是把每个位的数字相加,得出的数字如果不是一位数字,就再缩位,
直到变成一位数字为止。例如: 228 => 2+2+8=12 => 1+2=3
上面的数字缩位后变为:344836, 这就是程序最终的输出结果!
要求程序从标准输入接收数据,在标准输出上输出结果。
输入格式为:第一行是一个整数n(<100),表示下边有多少输入行,接下来是n行字符串,就是等待变换的字符串。
输出格式为:n行变换后的6位密码。

例如,输入:
5
zhangfeng
wangximing
jiujingfazi
woaibeijingtiananmen
haohaoxuexi

则输出:
772243
344836
297332
716652
875843

注意:
请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分!
在评卷时使用的输入数据与试卷中给出的实例数据可能是不同的。
请把所有类写在同一个文件中,调试好后,存入与【考生文件夹】下对应题号的“解答.txt”中即可。
相关的工程文件不要拷入。
请不要使用package语句。
源程序中只能出现JDK1.5中允许的语法或调用。不能使用1.6或更高版本。

分析
解题需要耐心、信心。
基本思想是将问题分解,大的问题分解成小的问题。
设计两个函数,一个负责计算字符串得出6个数字,一个负责缩位。

源码

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String[] ss = new String[n];
        for (int i = 0; i < n; i++) {
            ss[i] = sc.next();
        }
        sc.close();


        for (String s : ss) {
            System.out.println(f1(s));
        }

    }

    private static String f1(String s) {
        //确定字符数组的行数r
        int r = 0;
        if(s.length()%6==0) {
            r = s.length()/6;
        }else {
            r = s.length()/6+1;
        }

        //为字符数组a赋值
        char[][] a = new char[r][6];
        int index = 0;
        ok:for (int i = 0; i < r; i++) {
            for (int j = 0; j < 6; j++) {
                if(index == s.length()) {
                    break ok;
                }
                a[i][j] = s.charAt(index++);
            }

        }

        //计算每列的元素之和
        int[] b = new int[6];
        for(int j=0; j<6; j++) {
            int sum = 0;
            for (int i = 0; i < r; i++) {
                sum += a[i][j];
            }
            b[j] = sum;
        }

        StringBuilder sb = new StringBuilder();
        //将每个数字进行缩位处理
        for (int i = 0; i < b.length; i++) {
            b[i] = f2(b[i]);
            sb.append(b[i]);
        }

        return sb.toString();

    }

    //缩位处理
    private static int f2(int x) {
        if(x>=0&&x<10) {
            return x;
        }
        int sum = 0;
        String s = String.valueOf(x);
        for (int i = 0; i < s.length(); i++) {
            sum += (s.charAt(i)-'0');
        }
        return f2(sum);

    }
作者:bear_huangzhen 发表于2017/11/8 16:10:08 原文链接
阅读:0 评论:0 查看评论

一起学习Kotlin——Kotlin中的基本数据类型

$
0
0

Kotlin中的基本数据类型

作为Google【官方指定的干儿子】,Kotlin语言今年受到了越来越多的重视。无论是开发Android还是后台程序,这门语言以后一定是大有可为。由于相关的文章很多,那我们不多介绍这门语言是怎么来的。就让我们慢慢来,一点一点的去品味Kotlin。

第一章,一起来看看这门语言的基本数据类型吧。


开始之前,我们需要明确一点:

kotlin是一门【强类型】、【静态类型】、【支持隐式类型】的【显式】类型语言。

PS:
1、强类型:强类型语言的编译器引入了较多的类型检查限制,因此在运行时不会发生未经明确(显示转换)类型转换的类型转换。常见的语言中,Java就是强类型语言,而JavaScript则是弱类型语言。
2、静态类型:根据类型检查在编译时期还是运行时期,我们把语言分为静态类型以及动态类型。静态语言是基于编译器来确保语言类型的安全。
3、显式类型:根据变量名是否需要显式的给出声明,我们可以将语言分为显式类型以及隐式类型。kotlin是显式类型的语言,与此同时又因为有【类型推断】的作用所以可以看做是支持隐式类型。


一、简述

1、var 与 val

kotlin中,var符号表示的事变量,可以多次重复赋值。
而val表示的是“常量”,一次赋值之后,不能再次修改。


2、kotlin中的根类型:Any

kotlin中万物皆对象,所有类型都是引用类型。所有的类都有一个超类:Any。
官方文档是这么说的:

/**
* The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
*/

这个类只有三个方法:

public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String

如果类声明中,没有指明超类,则默认为Any。
kotlin中的Any映射为java中的java.lang.Object.

特别注意的一点:java中Object只是所有引用类型的超类,而基本类型int、long等不包含在内。而kotlin中,Any是一切的超类。


3、kotlin中的数字类型

3.1、常识介绍

kotlin中常见的数字类型分为以下6种:

方法名 转换类型 补充
Byte 8位 1字节
Short 16位 2字节
Int 32位 4字节
Long 64位 8字节
Float 32位 4字节
Double 64位 8字节

根据名称可以看出基本和java中的相关定义相近。
kotlin支持二进制、十进制、十六进制;

但【不】支持八进制

与此同时,kotlin在数值类型的表示方式上也支持下划线,如:
val Million = 1000_000_000
val tmp = 123_456_789L
val demo = 0xDD_FF_EE

kotlin中的数字类型与java很是相近,但有一点不同:

kotlin没有java语言的隐式变换(如byte->short->int->long 等)。

因此,java中的如下代码,在kotlin中是无法编译的:

short a = 1;
int b = a;

当然,在kotlin中应该这样描述:

val a : Int = 1
val b : Long = a

上述代码同样是无法通过编译的!

因此,遇到类型转换我们就应该考虑一下【显式转换】:
java中,我们一般会这样进行数值类型的显式转换:

int a = 1;
...
Long tmp = (long)a;

而kotlin中,我们可以这样:

val a : Int = 1
val b : a.toLong()

相关的方法汇总如下:

方法名 转换类型
toByte() Byte
toShort() Short
toInt() Int
toLong() Long
toFloat() Float
toDouble() Double
toChar() Char

这里需要注意一点的是:

与java不同,kotlin中Char类型仅仅表示字符,不能再被直接当做数字。 因此,Char类型的变量必须在单引号之间表示:’变量’。(CSDN博客这里显示有误。。。)

一段简单的java代码如下:

char a = 'c';
System.out.println(a+1);

此时打印的结果为【数字】100。
而作为对比,在kotlin中:

val a : Char = 'c'
println(a+1)

上述代码的打印结果为【字符】d

因此我们要明确:Kotlin中 Char类型由于不是直接当数值用,所以最后返回依旧是Char型。


3.2、一些需要注意的点

3.2.1、== 与 ===

kotlin中并没有单纯的数值,在我们定义每一个数值的时候,kotlin编译器都在默默的为我们将定义的数值封装成一个对象,以最大限度的解决空指针异常的问题。因此kotlin中数值的比较除了我们常见的 == 号之外还多了 === 。
当然,根据面向对象的定义也可以快速的了解:

符号名 作用 否定形式
== 【结构相等】比较两个具体的【数值的大小】是否相同 !=
=== 【引用相等】比较两个对象【在内存的存储地址】是否相同 !==

ps:
编译器中,== 等同于equal()方法。

举个栗子来看:

fun main(args: Array<String>){
    val a : Int = 10
    println(a === a) //此处返回的值为true
    val b : Int = a
    println(a === b) //此处返回的值为true,因为赋值操作是内存地址赋值,而内存地址相同,其中的数字值自然相同
    println(a == b) //此处返回的值为true,原因同上

}

【☆☆☆】考虑到装箱问题:数字装箱不保留【同一性】,但保留数值的【相等性】。

    val a: Int = 200
    println(a === a) //此处返回的值为true
    val b :Int ? = a
    println(a === b) //此处返回的值为false,因为这是两个对象,内存地址不相同
    println(a == b) //此处返回的值为true,数值相同

当然还有一个特殊情况,考虑到JVM虚拟机的常量池特性:

val a: Int = 20
    println(a === a) //此处返回的值为true
    val b :Int ? = 20
    println(a === b) //此处返回的值为true
    println(a == b) //此处返回的值为true,数值相同

PS:
【1】JVM虚拟机中维护着有符号整形常量池(-128,127),在这个范围里的数值都会直接使用常量池的内存地址,所以这个范围内的数值装箱后比较内存地址依旧是相等的。(常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References))
【2】kotlin中,?表示这个对象可能为空。因此这一句“val b :Int ? = a”
表示的意思除了普通的数字赋值外,还多了一层含义:如果被赋值对象为空,则开辟内存创建新的对象。

3.2.2、移位操作

在java中,移位操作的表示非常简洁,直接用符号即可表示:

符号名称 作用
<< 左移位操作
*>> 右移位操作(此处*为显示效果)
*>>> 无符号右移位

而在kotlin中,则没有这些简单的符号表示,取而代之的是相应的移位操作方法:

符号名称 作用
shl(bits) 左移位
shr(bits) 右移位
ushr(bits) 无符号右移位
and(bits) 与操作
or(bits) 或操作
xor(bits) 异或操作
inv() 反向操作
3.2.3、运算符 + 的重载

先来看一段代码:

val a = 10L + 1

很明显,上述代码中,+ 左右两个数分别为Long类型与Int类型。
那么问题来了,我们前面说过,kotlin中并不支持数字类型的隐式转换,而且上面表达式中的数字1并没有显示拓宽为Long类型,为何却准确无误呢???
答案很简答,那就是kotlin中对于符号“+”的重载。
符号重载

看一看到,源码中有很多重载的方法。
当我们执行:val a = 10L + 1
实质上是:
val a = 10L.plus(1)

值得注意的一点是:XX.plus()方法中传入的形参只能为数值类型!


4、kotlin中的字符类型

kotlin中的基本字符为Char类型,相关的知识点我们已经在前文讨论过。


5、kotlin中的布尔类型

布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。


6、kotlin中的字符串类型

kotlin中字符串是String类型。
与java类似,kotlin中的String同样是由final修饰的,即不可被继承。我们一起来看一些新的特性。

6.1、支持多行字符串

val string = """
            line 1
            line 2
            lin3 4
            """

注意以上代码的打印结果是存在前置的空格号的:
打印结果1

我们可以借助与 trimMargin() 发放做到去除掉每行的前置空格。

val string = """
            |line 1
            |line 2
            |lin3 4
            """.trimMargin("|")

这里有一点需要注意:trimMargin()方法需要传入一个String类型的参数作为删除前置空格并进行每行分割的标志Flag,我们在这里选用了”|”。

6.2、支持字符串模板

在kotlin中,字符串可以包含字符串模板用来表示前面用的到的一段代码。kotlin中规定,所有的字符串模板必须以美元符号”$”开头。

 val a = "temp"
 val q = "$a.length():"
 println(q+a.length)

打印结果为:
打印结果2

原生字符串和转义字符串内部都支持模板。

6.3、索引运算符s[i]

当我们需要对一个字符串的某一位取值时,java中会用到:

"abc".charAt(i);

而在kotlin中,我们可以简单的用索引运算符即可:

val s = "abc"
s[0]//结果为a

6.4、for循环迭代字符串

    for (s in "12345"){println(s)}

打印结果:
打印结果3


7、kotlin中的数组类型

7.1、生成数组

kotlin中的数组不是new出来的而是用了别的表示方法:
【1】fun_1

    val a = arrayOf(1,2,3) //1,2,3

【2】fun_2 工厂函数

    val b = Array(3, { i -> (i * 2) })//0,2,4

7.2、数组的多类型表示

kotlin中的数组支持多类型:

    val arr = arrayOf(1,2,true,"abc")
    for(c in arr){println(c)}

上述代码的打印结果为:
打印结果12
注意:在这里kotlin编译器自动将数组升级为java.lang.Object类型了。
打印结果123

7.3、新建空数组

除此之外在新建空数组时,我们必须显示地知名数组元素的类型:

    val b = arrayOfNulls(10)//错误
    val b = arrayOfNulls<Int>(10)//正确

7.4、数组转换

注意:kotlin中数组不是形变的,即我们不能将Array赋值给Array。


作者:xiaoyaozaimz 发表于2017/11/8 16:12:41 原文链接
阅读:13 评论:0 查看评论

5 分钟带你弄懂 k-means 聚类

$
0
0

聚类与分类的区别

分类:类别是已知的,通过对已知分类的数据进行训练和学习,找到这些不同类的特征,再对未分类的数据进行分类。属于监督学习。

聚类:事先不知道数据会分为几类,通过聚类分析将数据聚合成几个群体。聚类不需要对数据进行训练和学习。属于无监督学习。

关于监督学习和无监督学习,这里给一个简单的介绍:是否有监督,就看输入数据是否有标签,输入数据有标签,则为有监督学习,否则为无监督学习。更详尽的解释会在后续博文更新,这里不细说。

k-means 聚类

聚类算法有很多种,K-Means 是聚类算法中的最常用的一种,算法最大的特点是简单,好理解,运算速度快,但是只能应用于连续型的数据,并且一定要在聚类前需要手工指定要分成几类。

K-Means 聚类算法的大致意思就是“物以类聚,人以群分”:

  1. 首先输入 k 的值,即我们指定希望通过聚类得到 k 个分组;
  2. 从数据集中随机选取 k 个数据点作为初始大佬(质心);
  3. 对集合中每一个小弟,计算与每一个大佬的距离,离哪个大佬距离近,就跟定哪个大佬。
  4. 这时每一个大佬手下都聚集了一票小弟,这时候召开选举大会,每一群选出新的大佬(即通过算法选出新的质心)。
  5. 如果新大佬和老大佬之间的距离小于某一个设置的阈值(表示重新计算的质心的位置变化不大,趋于稳定,或者说收敛),可以认为我们进行的聚类已经达到期望的结果,算法终止。
  6. 如果新大佬和老大佬距离变化很大,需要迭代3~5步骤。

说了这么多,估计还是有点糊涂,下面举个非常形象简单的例子:
有6个点,从图上看应该可以分成两堆,前三个点一堆,后三个点另一堆。现在我手工地把 k-means 计算过程演示一下,同时检验是不是和预期一致:
这里写图片描述

1.设定 k 值为2

2.选择初始大佬(就选 P1 和 P2)

3.计算小弟与大佬的距离:

这里写图片描述
从上图可以看出,所有的小弟都离 P2 更近,所以次站队的结果是:

A 组:P1
B 组:P2、P3、P4、P5、P6

4.召开选举大会:

A 组没什么可选的,大佬就是自己
B 组有5个人,需要重新选大佬,这里要注意选大佬的方法是每个人 X 坐标的平均值和 Y 坐标的平均值组成的新的点,为新大佬,也就是说这个大佬是“虚拟的”。因此,B 组选出新大哥的坐标为:P 哥((1+3+8+9+10)/5,(2+1+8+10+7)/5)=(6.2,5.6)。
综合两组,新大哥为 P1(0,0),P哥(6.2,5.6),而P2-P6重新成为小弟。

5.再次计算小弟到大佬的距离:
这里写图片描述

这时可以看到P2、P3离P1更近,P4、P5、P6离P哥更近,所以第二次站队的结果是:

A 组:P1、P2、P3
B 组:P4、P5、P6(虚拟大哥这时候消失)

6.第二届选举大会:
同样的方法选出新的虚拟大佬:P哥1(1.33,1),P哥2(9,8.33),P1-P6都成为小弟。

7.第三次计算小弟到大佬的距离:
这里写图片描述
这时可以看到 P1、P2、P3 离 P哥1 更近,P4、P5、P6离 P哥2 更近,所以第二次站队的结果是:
A 组:P1、P2、P3
B 组:P4、P5、P6

我们可以发现,这次站队的结果和上次没有任何变化了,说明已经收敛,聚类结束,聚类结果和我们最开始设想的结果完全一致。

K-Means 聚类 MATLAB 实现

关于 K-Means 的算法具体代码,网上有各种版本,这里也不赘述了,下面结合 MATLAB 中的一些函数给出一个较为简洁的版本:

X2 = zscore(X);      % zscore方法标准化数据  
Y2 = pdist(X2);      % 计算距离(默认欧式距离)
Z2 = linkage(Y2);    % 定义变量之间的连接,用指定的算法计算系统聚类树
T = cluster(Z2,6);   % 创建聚类
H = dendrogram(Z2);  %作出系谱图

最终聚类系谱图如下所示:
这里写图片描述

当然,MATLAB 也提供了 kmeans() 函数可供直接聚类使用,详情可参与其文档。

作者:huangfei711 发表于2017/11/8 16:16:23 原文链接
阅读:0 评论:0 查看评论

0基础lua学习(十六)lua的多态 base

$
0
0

多态base

使用lua实现C++类似的多态,看起来略微难了一些,这个demo,默认提供了 init类似构造函数的时机。

--base.lua代码
--保存类类型的虚表
local _class = {}
function BaseClass(super)
    -- 生成一个类类型
    local class_type = {}
    -- 在创建对象的时候自动调用
    class_type.__init = false
    class_type.__delete = false
    class_type.super = super
    class_type.New = function(...)

        -- 生成一个类对象
        local obj = {}
        obj._class_type = class_type

        -- 在初始化之前注册基类方法
        setmetatable(obj, { __index = _class[class_type] })

        -- 调用初始化方法
        do
            local create
            create = function(c, ...)
                if c.super then
                    create(c.super, ...)
                end
                if c.__init then
                    c.__init(obj, ...)
                end
            end

            create(class_type, ...)
        end

        -- 注册一个delete方法
        obj.DeleteMe = function(self)
            local now_super = self._class_type
            while now_super ~= nil do
                if now_super.__delete then
                    now_super.__delete(self)
                end
                now_super = now_super.super
            end
        end

        return obj
    end

    local vtbl = {}
    _class[class_type] = vtbl

    setmetatable(class_type, {__newindex =
    --table key value
        function(t,k,v)
            vtbl[k] = v
        end
        ,
        __index = vtbl, --For call parent method
    })

    if super then
        setmetatable(vtbl, {__index =
            function(t,k)
                local ret = _class[super][k]

                return ret
            end
        })
    end

    return class_type
end
--main.lua代码
require"base"

father = father or BaseClass()
function father:__init()
    print("father:init")
end

function father:Bind()
    print("father:Bind")
end

function father:play()
    print("father:play")
end


son = son or BaseClass(father)

function son:__init()
    print("son:init")
end

function son:Bind()
    print("son:Bind")
end

function son:UnBind()

end


a = nil
a = son:New()
a:play()
a:Bind()

console:
father:init
son:init
father:play
son:Bind

多态base解析

1.首先要明确
father = father or BaseClass( )和 son = son or BaseClass(father)
有一个共同的表local _class = {},也就是虚表。

2.father = father or BaseClass(),我们会保存__class[class_type] = vtbl
classtype是key,value是表
返回的表是 class_type

                vtbl
                 {__newindex =
                    function(t,k,v)   
                        vtbl[k] = v
                    end,
                  __index = vtbl
                 }

3.function father:__init(),会将class_type表中的__init 从布尔,变为函数
4.function father:Bind(),没有Bind函数,所以会调用vtbl表中的__newindex,因为我们没有Bind的key

    function(t,k,v)   
        vtbl[k] = v
    end

这样vtbl中添加了一个Bind的key,value是函数
5.son = son or BaseClass(father)同理,但是它的vtbl会添加__index = function(t,k)的函数

6.function son:__init(),会将class_type表中的__init 从布尔,变为函数
7.function son:Bind(),所以会调用

    function(t,k,v)   
        vtbl[k] = v
    end

向son的vtbl表中加入函数
8.a = son:New()这是
son = son or BaseClass(father)里面的new方法,调用父类的init,然后调用子类的init,然后返回
_class[class_type],这个是son的vtbl表。(_class里面有两个vtbl,key分别是father的class_type和son的class_type,其值是各自的vtbl表)

9.a:play()这是调用父类的play方法,首先去son的vtbl表中查找,通过__index调用了

function(t,k)
 local ret = _class[super][k]
 return ret
end

_class[super]这是访问虚表中的,father的vtbl,然后我们找到了,然后进行调用。
注意:如果我们换成a:play1(),就会报错all method ‘play1’ (a nil value) ,也就是你声明函数,要加到父类的表里
也就是下面这步

function father:play()
    print("father:play")
end

10.a:Bind(),其实就是找son的vtbl,直接找到了函数,然后调用。

小结:

  • 我们声明的 son继承 father之前,需要创建两个表,每个表里都有一个vtbl , 有一个总表_class来管理
  • __newindex 实现的是,我们创建函数添加函数,加到vtbl 表里
  • __index 实现的是,继承的实现。即访问父类里面的函数,如果子类实现了,就访问子类的。
  • t 是table、k是key、v是value

看着是有点乱,但是你多看几遍,你就会有所收获。
这个代码可以说,我们在开发游戏的时候,使用的base代码。

作者:hiwoshixiaoyu 发表于2017/11/8 16:31:06 原文链接
阅读:3 评论:0 查看评论

系统架构师-基础到企业应用架构-系统建模[上篇]

$
0
0

一、摘要

       本文主要从系统架构中的建模开始讲解,本文讲述的内容主要是我在工作和学习过程中的总结和经验,不足之处还请大家多多批评指出,有更好的建议也可以留言

说明。本意主旨是为不熟悉系统架构建模过程和不知道如何使用建模工具,或者不熟悉如何根据需求去建立模型的角度出发,简单的阐述了在系统架构的过程中我们应

该从什么样的角度出发去分析需求并且建立抽象模型。这应该说是架构师必备的技能。

       本文由浅入深,本篇将简单的介绍如何使用使用UML建模中的各个结构图与行为图,去完成抽象模型的建立。

二、本章内容

       1、摘要。

       2、本章内容。

       3、建模工具介绍及使用。

       4、建模中的抽象模型图。

       5、本质总结。

       6、系列进度。

       7、下篇预告。

三、建模工具介绍

      介绍建模工具之前,我们先来简单介绍下建模语言的定义。建模语言就是基于一系列规则、符号、图表、关键字的图形化或文本语言。建模语言的主要作用是对模

型的结构与行为进行描述。并且能够将知识和信息通过模型传递给熟悉该描述语言的人。

      当今的建模语言其实并不少,其中比较有规模的如下图:

      image

     不过最流行、最常用的当属UML建模语言(Unified Modeling Language) 统一建模语言。经过不断的发展,目前UML已成为业界公认的标准的建模语言。

     我们先来了解下UML建模语言的起源:

     回顾20世纪晚期--准确地说是1997年,OMG组织(Object Management Group对象管理组织)发布了统一建模语言(Unified Modeling Language,

UML)。UML的目标之一就是为开发团队提供标准通用的设计语言来开发和构建计算机应用。UML提出了一套IT专业人员期待多年的统一的标准建模符号。通过使用

UML,这些人员能够阅读和交流系统架构和设计规划--就像建筑工人多年来所使用的建筑设计图一样。

     到了21世纪--准确地说是2003年,UML已经获得了业界的认同。在我所见过的专业人员的简历中,75%都声称具备UML的知识。然而,在同绝大多数求职人员面

谈之后,可以明显地看出他们并不真正了解UML。通常地,他们将UML用作一个术语,或对UML一知半解。大家对UML缺乏理解的这种状况,促进我撰写这篇关于UML

建模。当阅读完本文时,您还不具备足够的知识可以在简历上声称自己掌握了UML,但是您已具有了进一步钻研该语言的良好起点。

四、建模中的抽象模型

     既然UML语言如此流行,本系列中也只用UML语言来进行建模,本系列中的后续章节也将基于UML建模图来完成相应的设计。

     学习过UML语言的开发人员都知道UML分为以下几类模型图:

     image

     通过上图我们知道UML的分类,分为结构型与行为型建模图形。下面的内容将详细的讲述每种建模图形的使用场景及如何使用。

    行为型:

     我们先从行为型的建模图形来开始讲起:

    1、用例图:

     我想用例图大家都应该基本上有所了解,只要使用过UML建模的除了基本的流程图基本上大家都会的使用外,用例图用过是最常见的一种建模图形。

     用例图中主要包含的元素:系统、参与者、用例、关系。

     用例图主要的应用场景:一般用例图用来描述需求中的系统应具有的功能,系统参与者(使用者,维护者、外部系统或者用户等)与系统如何交互进行一个模

型话的描述。

     用例图的目的:帮助开发团队以一种可视化的方式理解系统的功能需求。

     一般使用如下方式来进行操作:

     image 用来标识系统的参与者,任何与系统交互的对象,都可以叫参与者。

     image

     是用来描述系统中的某个模块与参与者的一次交互过程。

     系统参与者与用例之间的具体关系通过如下连线标示:

     image

     这几类不同的连线来标识不同的用例之间或者用例与参与者或者2个参与者直接直接的关系。

     UML定义了3类标准的关系:

     第一种:包含,通过一条直线链接2个用例,因此是用例之间的关系链接,表述了箭头的开始一端包含箭头指向的一端的用例。

     例如:image

     第二种:扩展,通过一个反向的直线来标识某个用例扩展了另外一个用例的行为,一般情况下箭头指向的用例即是被扩展的用例。

     例如: image

     第三种:泛化,用来标识具有同质关系的参与者与参与者或者用例与用例之间的关系,泛化类似继承关系。箭头指向的为父元素。

     例如:image

     除了以上的3中关系还有一种未列在规范关系的我们把它叫做关联关系。这种关系是用来描述用例与参与者直接的关系的。是通过一条直线来完成链接的,泛化关系

描述了链接的2个部分存在某种程度的交付。一般情况下,我们可以系统的功能情况分析出系统中的主动发和被动方。

     如何使用用例图:

     第一步:先把系统按照功能进行划分,比如一个简单的内容管理系统。先把他细化,细化成多个模块功能。每个模块的功能相对独立,但是可能又与另外一个有交

互。

     第二步:把功能需求抽象,达到高内聚,低耦合的标准,然后分析出该模块功能的参与者是什么,例如用户是谁?或者细分成角色,与该模块交互还可能是数据库?

等,把所有交互的对象分析出。

     第三步:把系统模块中的每个功能模块看是否能再按照子功能进行细分,细分后形成具体的用例。

     第四步:分析用例与参与者之间的关系,分析同质对象(参与者与参与者、用例与用例)之间的关系。

     第五步:根据以上四步完成建模。在建模的过程如果发现某块功能不清晰或者参与者不清晰,可重复前4步。

    2、类图:

     类图也是UML建模中最常用的一种结构图,类图用来标示系统的静态结构。静态结构是由类型及关系构成。

     类图表示不同的实体(人、事物和数据)如何彼此相关;换句话说,它显示了系统的静态结构。类图可用于表示逻辑类,逻辑类通常就是业务人员所谈及的事物种

类--摇滚乐队、CD、广播剧;或者贷款、住房抵押、汽车信贷以及利率。类图还可用于表示实现类,实现类就是程序员处理的实体。实现类图或许会与逻辑类图显示一

些相同的类。然而,实现类图不会使用相同的属性来描述,因为它很可能具有对诸如Vector和HashMap这种事物的引用。

     类图其实就是一个长方形,内部分成3个区域。每个区域的含义不同。

     image

      类图中也有命名空间的概念,是通过包来实现的如果想定义该类在某个命名空间中,则在定义类名时按照如下类似格式标示

      命名空间 :: 类名 [必须按照这样的形式才可以]。

      类图中的有3类修饰符,每种修饰符标示的含义不同。

      image

      具体用法如下:

      image

      理解成具体的类代码的格式如下:

      public class Product

      {

          Public string ProductName;

          public void GetProductLists(string sWhere)

          {

             //TODO….

          }

      }

      如果在类图中的属性定义与函数成员的定义是斜体表示的话,则表名该成员是虚成员。

      image 虚成员

      如果在类图中的属性定义与函数成员的定义是带下划线的话,则表名该成员是静态成员。

      image 静态成员

      当然这是最基本的类图,还有一种特殊的,类图支持参数化类型即是.NET中的特殊类型[泛型格式]标示。

      image 参数化类图

      具体的表示形式如:该符号在类的右上角有个长方形其中可输入类型如上图。

      类图中属性包含的元素:

      访问修饰符:Public、Protected、Private

      特性/属性名称:特性/属性名称

      类型:可以是自定义类型或者是系统类型。

      默认值:即特性/属性的默认值,如果有的话。

      重复性:可以用来定义多个对象的集合,特性值中包含的对象个数。

     类图中操作包含的元素:

      访问修饰符:Public、Protected、Private

      操作名称:函数名称

      操作列表:函数的参数列表。

      返回值:函数的返回值,如果有的话。

      函数参数列表中的参数方向:

      image

     类图之间的关联关系

      首先我们知道,我们在设计类的时候就是把独立的功能放在一个类中,不同的类之间进行交互,那么我们在类图中如何去表述这样的类之间的关系呢?

      类图直接的关系:

      1、关联关系:关联标识2个类直接存在关系。是通过一条线来表示,关联关系中包含了2种特殊的关系:聚合和组合

       聚合代表的2个类直接是has-a的关系,即部分与整体的关系,具体的图标通过一条虚线带有菱形箭头,箭头指向的方向即是整体的部分,代表该类包含另一部分。

      聚合例如:image 代表产品中具有ProductName这个成员。

      组合举例:组合关系的标示与聚合比较类似,唯一区别实心的菱形。

      组合例如:image

      组合与聚合的区别:

      在聚合关系中被包含对象可能不完全依赖容器对象,也就是说ProductName不完全依赖Product。如果Product对象销毁,但是可能ProductName对象没有被销

毁。可以这么想想产品的分类不会因为产品销毁而不存在。

       组合关系中则是比聚合的关联程度更高,Product完全包含ProductName。如果销毁Product时,那么ProductName也一定被销毁。产品从数据库被删除了,那

么与产品相关的的数据列属性也被删除了,这里只是举例子,可能不太合适。     

      类图之间的泛化关系

       泛化关系:存在2个类之间。一个类是另外一个类的子类,表示一个类是另外一个类的特例。

       表示方法:通过一个带有空的三角形箭头的线段标识,箭头指向父类型。

       image 表示火车和汽车是交通工具的子类型。

      类图之间的依赖关系

        依赖关系描述为:一个类型必须依靠另外一个类才能实现相应的功能。最简单的理解方式:依赖注入中的构造函数注入。

        具体的表示方法:一个带有箭头的虚线段。箭头方向标示被依赖的类型。

        例如:image

五、本章总结。

       本章主要是对UML有个简单的介绍及详细介绍了如何构建UML图形中的用例图与类图。这是我们在建模时常用的2类图形。也是必须掌握的建模图形。

同时通过本质我们应该大脑中对UML有个新的认识,UML建模可以让我多个角度的去分析问题,然后不断的改进设计,同时能很清晰的表达功能需求功能的分离和组合

关系。本文只是简单的抛砖引玉,不足之处,在所难免,请大家批评指出。

六、系列进度。

       1、系统架构师-基础到企业应用架构系列之--开卷有益

       2、系统架构师-基础到企业应用架构-系统建模[上篇]

       3、系统架构师-基础到企业应用架构-系统建模[中篇](上)

       4、系统架构师-基础到企业应用架构-系统建模[中篇](下)

       5、系统架构师-基础到企业应用架构-系统建模[下篇]

       不断更新中(请持续关注…)

七、下篇预告。

      下一篇中将介绍UML建模过程中其他的比较常用的UML建模图形:顺序图、组件图、状态图等。

作者:hegezhou 发表于2011/9/10 12:23:01 原文链接
阅读:2335 评论:2 查看评论

系统架构师-基础到企业应用架构-系统建模[中篇](上)

$
0
0

一、上章回顾

       上篇文章主要简单的介绍了建模中使用的标准建模语言UML的相关内容,包括用例图与类图的使用方法及如何建模。相信大家对UML建模语言已经有了初步的认

识,还请大家谨记UML不同的建模图形的用处。比如,用例图主要用来描述系统的功能需求。类图主要用来描述实体间的关系。谨记这些就可以帮助我们在系统架构的

过程中深入的分析。

       首先向大家道歉,上篇中有部分描述错误的地方,可能对大家造成一定的错误引导。

       image 这是上篇给出的图,我描述的是组合关系。

       特别更正为:

       image 这是正确的结果。箭头指向聚合类。描述的信息并无任何错误。希望能对大家指正。

二、摘要

       本文主要从系统架构中的建模开始讲解,本文讲述的内容主要是我在工作和学习过程中的总结和经验,不足之处还请大家多多批评指出,有更好的建议也可以留言

说明。本意主旨是为不熟悉系统架构建模过程和不知道如何使用建模工具,或者不熟悉如何根据需求去建立模型的角度出发,简单的阐述了在系统架构的过程中我们应

该从什么样的角度出发去分析需求并且建立抽象模型。这应该说是架构师必备的技能。

       本文由浅入深,本篇将简单的介绍如何使用使用UML建模中的各个结构图与行为图,去完成抽象模型的建立。

       本文主要讲解以下几个建模图形:顺序图、组件图、状态图、活动图、部署图。当然本文也只是讲述了基本理论介绍及如何设计使用,系统架构师-基础到企业应

用架构-系统建模[下篇] 将会详细的讲解通过具体实例讲解如何使用这些已经介绍的抽象模型图形去描述。

三、本章内容

       1、上章回顾。

       2、摘要。

       3、本章内容。

       4、建模中的抽象模型图。

       5、本章总结。

       6、系列进度。

       7、参考文献。

       8、下篇预告。

四、建模中的抽象模型图。

1、顺序图。

介绍

       顺序图也称序列图,主要用来系统中的某个流程的详细步骤。顺序图能够给出流程中一系列对象的交互顺序。通过顺序图可以让我们更好的了解如何实现某个用例

的方法。我们知道用例图用来描述系统的功能需求。而顺序图清晰的描述了某个用例也就是系统功能的的实现方法。

详解

       在顺序图中包含的元素:

       image

       对象:用来标识流程中的详细步骤中的对象。

       活动条:用来标识当前对象是活动的,如果想表示某个对象是活动的,那么必须使用一个虚线+活动图的形式来构建。

       例如我们现在要标示一个简单的做公交车的刷卡流程:

       image IC卡刷卡操作。

       相关解释说明:

       公交卡,首先放在刷卡终端上,终端读取卡中的余额信息,然后刷卡终端与终端中的扣款程序对象交互,扣款程序根据读取的余额信息,与刷卡终端中的固定刷卡

金额对比,如果当前IC卡的余额大雨刷卡终端的固定金额则,扣除金额,并且返回一个消息,提示刷卡成功的操作。

       途中的实线表示调用被调用对象的方法,虚线表示当被调用对象执行成功后,返回的虚线上表示返回值的逻辑名称,这样可以提高了可读性。

       在公交卡与活动条之间,应有一个虚线链接。

       在上图中我们使用了活动条,活动条作为生命线的一部分。我们并没有定义对象的创建和销毁,因此我们来看UML建模语言提供的描述对象的创建与销毁实例。

image

       上图中的X符号的图标代表的时候对象的销毁。创建对象通过new来创建,上图中,我用中文描述“创建对象”来完成对象的创建,那么在生命线下的的X符号代

表销毁对象,从内存中移除对象。当然这个对象的销毁对不同的开发语言有这不同的处理方式。C++中的销毁对象,必须调用析构函数来销毁对象。C#与JAVA语言中

则只是说明当前需要销毁的对象没有被其他的对象引用,那么这类语言编译器提供垃圾回收器来完成回收。

       注意:当某个对象引用了另外一个对象,该对象有责任销毁被引用对象并且必须显示销毁该被引用对象时,那么必须要显示的发送被引用对象销毁的通知消息。白

话文来说就是显示的调用被引用对象的销毁方法。

       顺序途中的同步与异步。

       顺序图中的同步与异步与我们平时书写代码中的同步与异步的解释意思差不多。这里不过多解释,通过图例说明:

       image 客户去餐厅吃饭,首先要点餐,必须等待点餐完了才能上菜。意思就是可以这样简单描述。A简单调用B方

法,必须等待,等到B方法执行完毕后,继续执行。

      image 函数A调用函数B,如果B需要的时间特别长,那么此时A可以去继续执行做其他的事情比如做和函

数C交互,等B函数执行完了,只需要回调通知A,B函数执行完了即可。在函数调用中的术语就是回调。

      UML建模语言中同步与异步消息的标识格式:

      image

      UML提供了一些顺序图的高级功能:例如可以通过顺序图实现流程的控制。具体的实现工具是通过UML提出的交互框来实现流程条件的控制。

      交互框其实就是定义了流程控制图中的控制逻辑,基于交互框定义流程执行的条件。如果满足这个条件,那么则执行交互框中已定义好的顺序步骤。否则不做任何

操作。交互框中除了定义流程控制的条件外,还有一些自己特殊的操作符,具体的操作符及其作用,如下列表:

      image

    每个关键字代表的含义都有相应的描述。大家应该都可以看明白,上述的所有含义都是针对交互框来说的。

   总结

    如果在系统功能中有特殊需求,那么顺序图中的交互框是可以支持嵌套的。嵌套交互框的话,会提高顺序图的复杂度,降低可读性。因此我们设计时的原则尽量把复

杂的流程拆分成几个简单的,分别绘制顺序图来完成相应步骤。

  2、组件图。

简介

    众所周知,组件图是用来描述系统中的各组件之间的关系。首先我们必须知道组件的定义是什么,然后组件之间有哪些关系。理清楚这些,我们在以后的设计中才能

派上用场。UML语言对组件的定义已发生了巨大变化。在之前的版本里面,UML如下定义组件的:

   UML1.1语言中对组件的描述:把某个文件或者可以运行的程序称之为组件。但是我们知道,UML出现组件图以前,组件一般用来描述COM组件或者其他的组件,因此造成冲突,所以随着后续UML语言的发布,修改了原有的含义。

   UML2.x语言中对组件的的描述:组件是独立的,是运行在一个系统中的封装单位,提供了一系列的服务。

   通过上述UML语言中的变迁,目前的理解是:一个系统,可以随意更换系统中的某个组建。而不会影响系统的运行。这可以理解为类似,大家熟悉IOC容器的都应该

知道,运行在IOC容器中的对象,可以看作组件,那么替换其中的提供某一服务的组件,只要满足该组件服务的相关契约就能自动完成替换。而不会影响系统的运行。

每个组件都封装了一些特殊的行为,实现了特定的服务。

   组件之间的关系有哪些呢?我们通过下图来看看,组件直接可能存在的关系:

   image  组件直接的关系基本上来说就这2种。下面会举例区别2中关系。

   组件图提供的服务:组件图为系统架构师提供了解决方案的自然形式。组件图允许架构师验证系统的必需功能是由组件来完成的。组件是可以复用的。

详解

   组件图中包含的元素:

   image

   下面我们分别讲解:

   (1)、组件:我们知道组件是组件图中最基本的组成元素,组件上面已经讲述了组件的定义。这里就不在多介绍,组件图组成的基本单位即组件。

   (2)、容器:可以为多个组件提供服务的管理容器,容器中的组件相互交互。

   (3)、包:可以看作一个子系统,其实也可以看作是特殊的组件。

   (4)、约束:用于定义接口规范。

   (5)、给组件图中的相应元素添加相应注释信息。

    image 组件上可以定义自己的接口。例如上图,人这个组件提供了2个接口。Thinking与Sleep接口。

   组件关系的建模:

   我们来看看组件之间的关系的表示,根据上面讲解的组件的关系有依赖和泛化,参考类图中的依赖和泛化。

   image 依赖关系,标识一个组件依赖另外一个组件,如果被依赖组件无法正常运行,那么该组件也无法运行。

  image 泛化关系。标识一个组件与其他多个组件的关系为继承关系。

总结:

   通过上面的学习我们知道:组件图主要是为系统架构师对整个系统的解决方案的自然形成,可以通过组件图的形式把系统的大体功能进行区分和设计。通过组件图把

系统功能进行抽象和分离。然后通过顺序图把功能流程细分成多个步骤,然后通过类图去构建每个流程步骤中的每个类应具有的个方法。最后形成一个完整的设计文

档。

  3、状态图。

简介

  状态图其实是针对一个对象(实体、组件其他元素等)来说的。主要是描述了,对象的行为如何改变状态的反映。我们研究UML状态图的目的就是为了搞清楚,对

象状态的变化与行为的关系。建模对象的实时行为。创建状态图的条件:当对象行为的改变与状态有关的时候才创建状态图。状态图反映了对象如何改变状态相应行为

的变化和展示对象的生命周期的创建和删除的全过程。

详细

  状态图可建模的对象:

  image

  用例:可以描述用例图中的某个用例状态的变化。

  类:可以描述某个类的状态的变化。

  子系统:可以描述某个子系统中状态的变化。

  整个系统:类似(WF)工作流中的流程,每个节点其实就相当于一个状态。

image

  上面简单的绘制了一个去餐厅吃饭的状态变化,每个状态变化的行为都有描述,当然我这里只是简单的举例说明状态图的变化,并没有详细分析的所有可能状态都画出来。

具体的状态还请大家自己练习画出来,此处只是简单的举例说明。

  状态图中的元素:

  状态标记:

  image

  状态图中可以标识一个或多个初始状态,也可以包含一个或多个结束状态。

  状态图中不同状态之间的关系:

  image 转移关系,用来描述对象的状态从一个状态转移到另外一个状态的处理流,箭头指向转移后的状态。

   状态图中提供了类似流程图中的判定的功能元素:决策点。

   通过元素决策点来完成:

   image 决策点,用来决策跳向不同的状态。

   具体用例如下:

   image 就是起到了一个决策的作用。这里不在复述。

   状态图中的同步:

   状态图中的同步主要是为了说明并发工作流的分岔和联合。下图描述了状态图中的同步条:

image

    初始状态进入到同步条中分岔同步执行操作A与B,分别进入A状态、B状态,然后分别执行A1,B1联合进入到结束状态。

    一个对象可以通过同步操作同事拥有多个状态。有时候一个对象还可以拥有不同层次的多个状态。当单个状态拥有独立的附加子状态时就可以在状态图中使用层次结

构的状态。

   组合状态就是这样的比较复杂的状态结构图,有时候我们需要把一个复杂的状态细化成多个子状态的合成,那么这个复杂的状态就可以叫组合状态。

   下面举例说明:

image

  组合状态B,也即复合状态B,内部可能有比较复杂的状态(C-D状态)。这种只是组合状态B中存在单个状态变化流程的情况,还可能组合状态B中包含更多的状态流。

  那么我们就要用如下的状态图完成:

image

   上图中1代表的是下单的流程,2代表付款流程。

总结

   通过上面的学习我想大家对状态图有了一定的了解,那么我们来总结下,如何建模状态图。

   第一步:我们知道建模状态图,首先需要抽象出来要建模的对象。

   第二步:我们基于这个对象分析出该对象具有的所有状态及发生状态改变的行为。

   第三步:标识每个对象状态的起始状态与结束状态。

   第四步:开始创建对象的状态图,分析是否有必要创建复杂的组合状态。

   系统架构设计的过程中,我们首先要分析出哪些对象需要使用状态图来描述。如果某个对象具有复杂的行为,那么可以使用活动图来建模比使用状态图更适合。每个

状态图必须至少有一个起始状态和结束状态。并且详细的分析对象发生状态改变的行为。从某个状态转移到另外一个状态的行为是什么。在某些情况下,如果对象的某

个状态无法清晰的表达时,可以通过创建组合状态来进一步细化该状态,这样能更清晰的表达对象的状态变化。

五、本章总结。

  本章主要讲述了UML建模图形中的顺序图、状态图、组件图。并且分析了什么情况下使用各种UML建模图进行建模。并且通过简单实例说明如何使用。等UML所有的

建模图形介绍完毕后,我将针对如何我目前遇到一些问题进行分析讲解,如何遇到功能需求进行功能的分离及建模。希望大家多多提出宝贵意见。

六、系列进度。

       1、系统架构师-基础到企业应用架构系列之--开卷有益

       2、系统架构师-基础到企业应用架构-系统建模[上篇]

       3、系统架构师-基础到企业应用架构-系统建模[中篇](上)

       4、系统架构师-基础到企业应用架构-系统建模[中篇](下)

       5、系统架构师-基础到企业应用架构-系统建模[下篇]

       不断更新中(请持续关注…)

七、参考文献。

八、下篇预告。

     下一篇将把本章没有讲述完毕的活动图与部署图讲解完毕,其他的不常用的建模图形可能只是简单的讲解,不会像这几篇文章那样具有说明的讲解。由于本人才疏

学浅,可能对UML建模的认识不够深入,还请各位多多支出宝贵意见,我将在后续的文章中不断的改进和学习,将自己掌握的内容写出来,一方面是帮助不熟悉UML的

朋友尽快的上手,另外也可以让自己加深印象。

作者:hegezhou 发表于2011/9/10 12:24:19 原文链接
阅读:2014 评论:0 查看评论

系统架构师-基础到企业应用架构-系统建模[中篇](下)

$
0
0

一、上章回顾

        首先、我们先来回顾下,上篇讲解的内容,加深下印象。上篇我们主要讲解了3个建模图形分别是:顺序图(序列图)、组件图、状态图。

        具体功能描述如下图:这里不详细解释,如果不清楚请看:系统架构师-基础到企业应用架构-系统建模[中篇](上)

        image

         由于全部放在一篇中篇幅太长了,所以分开讲解。

二、摘要

       本文主要讲解:UML建模图中的活动图、部署图等

       image

       上图中就是本章要讲解的内容,本质将仔细的剖析,部署图与组件图的关系与区别,活动图与状态图的关系与区别。

三、本章内容

       1、上章回顾。

       2、摘要。

       3、本章内容。

       4、建模中的抽象模型图之部署图、活动图。

       5、本章总结。

       6、系列进度。

       7、下篇预告。

四、抽象模型图之部署图、活动图

部署图

      首先,我们先来讲解部署图。部署图主要是用来描述一系列组件部署到节点运行的结构。部署图显示了系统运行时的结构。一般情况下部署图帮助我们来理解分布

式应用系统。同时部署图还传达了构建应用系统的软件与硬件元素的配置及部署方式。

      部署图中的基本元素:

      1、节点:这里就是指组件运行的环境。可以是软件(操作系统、其他等)或硬件资源(计算机,其他硬件)。

      UML建模语言中的通用图形化表示为:

      image

       2、节点实例:节点实例与节点的区别就是有下划线和冒号,节点实例必须紧跟冒号,当然这个节点实例名称可以为空,节点必须要有。

      image 

      3、组件容器:一个节点可以包含其他节点,可以是组件,也可以是节点。

      image

      4、节点之间的关系

        (1)、单向依赖:

          image

         上图表示 查询统计组件,通过.net提供的ADO.NET访问SQLServer2005数据库。

         (2)、双向依赖:

          image

          上图表示:产品管理模块会把数据写入到数据库中,同时产品管理中的信息会从数据库中读取,双向依赖。

         (3)、通信:

          image

          上图表示:应用软件系统与数据库通过.NET提供的方式相互通信,个人理解任务就是双向通信(双向依赖)[错误之处,还请高人指出]。

        5、实例讲解:

        下面我们已一个简单的系统B2C来进行讲解:

        我们先来看看B2C系统中的相应节点:

        image

        客户端通过浏览器访问B2C站点,首先进入会员管理,如果注册,则进入到注册系统。会员管理中完成对采购的管理、支付、发布等。

        节点描述:

        浏览器:通过键入网站地址访问B2C站点。这是与B2C系统交互的唯一入口。

        注册系统:完成用户的注册与数据库通信。图上并未画出,所有的节点除了浏览器不需要直接与数据库交互外,其他的模块都需要与数据库通信。

        会员管理:完成会员中心的管理。会员的个人信息,开店的店铺信息,收货地址等等信息的管理,我的采购,我发布的产品等等。

        采购系统:系统中的子功能,用于完成买家的产品采购。

        发布系统:主要为卖家提供服务,发布产品信息等。与数据库通信

        支付系统:完成支付交易的操作。与个人账户进行通信。

        当然这里只是举个简单的例子,其他的内容,比如前台的展示等等,这些目前都没有考虑其中,也没有仔细分析,这里只是达到介绍的目的。

        6、总结

       通过上面的讲解相信大家对部署图已经有了基本的认识,部署图主要是用来完成将组件部署到节点上运行的结构。从整体上描述了,系统运行时的结构。部署图是

必须要掌握的建模图。

活动图

        活动图主要是用来描述系统的动态行为,从一个活动到另一活动的控制流。活动图的本质是流程图,但是与流程图又有所不同。在本小节中将会详细的讲解活动

图与流程图的本质的区别及活动图与状态图的区别。

        按照惯例,我们先来看看活动图的元素:

        1、动作状态:

        image

        通过用圆形边的长方形来表示一个动作状态。动作状态有几个特点:原子性(要么执行,要么不执行)、不可中断的操作,并且此次动作完成后一定转向到另外一种

状态。 动作状态是构造活动图的最小单位。

        状态图区别:

        a、活动图中动作状态可以有入转换与出转换,意思就是说可以从当前状态转向到另外一个状态,也可以从另外一个状态转换到当前状态。图形化的表示如下:

        image B动作状态,可以有入转换A,出转换C。

         动作状态必须至少有一个出转换,转换都是以内部的完成为起点,与外部事件无关。

         实心圆:代表起始状态。

         环形内的实心圆:代表结束状态。

         b、动作状态与状态图不同的是,动作状态不能有入口动作与出口动作。更不能有内部转移。

        2、活动状态:

        image

        通过二个半圆与一个长方形组合起来来标识活动状态。

        活动状态首先可以被分解成多个子活动或者多个子动作状态。活动状态他不像动作状态是原子性的。活动状态是非原子性。活动图内部的活动,可以用另外一个

活动图来表示。活动状态可以看作多个动作状态和多个子活动的组合。

        活动状态与动作状态不同,动作状态是活动状态的一个特例,当某个活动状态只有一个动作状态时,这个活动状态就是一个动作状态。活动状态可以有入口动作

和出口动作。还可以有内部转移。因为活动图是多个子活动和多个动作状态的组合,所以本来动作状态直接的转向就可以看作是内部转移了,所以就很好理解了。

        image

        上图已经基本表示出来了活动状态中的动态状态的转移等。我相信大家都能理解。

        3、动作节点之间的关系

        a、控制流:image 与状态图中的转向相同,活动图也使用一个带箭头的线段,箭头指向要转入的状态。

        b、分支:image   活动状态从A分支出来活动状态B、C,

        c、合并:image 活动状态B从活动状态A与C合并后得到。

        d、泳道:泳道将活动图中的多个活动划分成多个组。并且把每一组活动都由对象来负责组织业务,泳道区分了负责活动的对象。并且泳道明确的表

示了哪些活动是由哪些对象进行的。泳道通过垂直线来区分。而2个垂直线分割的区域即是一个泳道。上面的解释可能有点绕,说白了泳道即是上面说的对象,对象就是

泳道。把不同的泳道就叫一个对象。每个活动状态在有泳道的活动图中,只能属于一个泳道。

        下面来看有泳道的图例:

        image 上面有2个泳道,分别是我是泳道1,我是泳道,并且我是泳道1中的D与我

是泳道中的活动状态A有转向关系。

       e、对象流。

        对象流是对象与动作状态或者活动状态直间的依赖关系。表示动作使用对象或者动作对对象的影响。一般我们在使用中,我们可以把对象通过依赖关系与动作状态或者活动状态进行链接。

        对象流的几个特点:

        (1)、一般一个对象可以由多个活动状态或动作状态操作。

        (2)、一个活动状态或动作状态的输出对象可以作为另一个活动状态或动作状态的输入。

        (3)、一个对象可以在一个活动图中多次出现,但是有点需要注意,这个对象多次出现时表名该对象处于生命周期的不同时期。

        包含对象流的活动图:

        image

        泳道M1中出现了对象。并且该对象与活动状态B有依赖关系。

   总结

        本节中讲解了,活动图的基本知识,下面我们以我们平时比较熟悉的B2C业务,电子商城为例说明下,会员的产品管理流程。通过状态图的形式来表达。以巩固

下我们学习的成果。

        例如B2C中的产品管理。首先必须是会员才能登入系统中,然后必须是我是卖家,然后才能进行发布产品的操作。

        image 会员先要开启店铺,设置权限后才能进行产品管理

        image

五、本章总结

        本章主要讲述了部署图与活动图。现在我们回顾下本章要点。

        部署图:主要用来描述一系列组件部署在节点上运行的结构,是系统运行是的结构的描述。主要用于软件系统的

        活动图:主要用来描述系统的动态行为,从一个活动转换到另外一个活动状态。通过一系列的操作将业务流程通过工作流的形式来描述。一系列操作就是一系列

的活动状态。

六、系列进度。

前篇

       1、系统架构师-基础到企业应用架构系列之--开卷有益

       2、系统架构师-基础到企业应用架构-系统建模[上篇]

       3、系统架构师-基础到企业应用架构-系统建模[中篇](上)

       4、系统架构师-基础到企业应用架构-系统建模[中篇](下)

       5、系统架构师-基础到企业应用架构-系统建模[下篇]

      6、系统架构师-基础到企业应用架构-系统设计规范与原则[上篇]

      7、系统架构师-基础到企业应用架构-系统设计规范与原则[下篇]

      8、系统架构师-基础到企业应用架构-设计模式[上篇]

      9、系统架构师-基础到企业应用架构-设计模式[中篇]

      10、系统架构师-基础到企业应用架构-设计模式[下篇]

中篇

      11、系统架构师-基础到企业应用架构-企业应用架构

      12、系统架构师-基础到企业应用架构-分层[上篇]

      13、系统架构师-基础到企业应用架构-分层[中篇]

      14、系统架构师-基础到企业应用架构-分层[下篇]

      15、系统架构师-基础到企业应用架构-表现层

      16、系统架构师-基础到企业应用架构-服务层

      17、系统架构师-基础到企业应用架构-业务逻辑层

      18、系统架构师-基础到企业应用架构-数据访问层

      19、系统架构师-基础到企业应用架构-组件服务

      20、系统架构师-基础到企业应用架构-安全机制

后篇

      21、单机应用、客户端/服务器、多服务、企业数据总线全解析

      22、系统架构师-基础到企业应用架构-单机应用(实例及demo)

      23、系统架构师-基础到企业应用架构-客户端/服务器(实例及demo)

      24、系统架构师-基础到企业应用架构-多服务(实例及demo)

      25、系统架构师-基础到企业应用架构-企业数据总线(实例及demo)

      26、系统架构师-基础到企业应用架构-性能优化(架构瓶颈)

      27、系统架构师-基础到企业应用架构-完整的架构方案实例[上篇]

      28、系统架构师-基础到企业应用架构-完整的架构方案实例[中篇]

      29、系统架构师-基础到企业应用架构-完整的架构方案实例[下篇]

      30、系统架构师-基础到企业应用架构-总结及后续

七、下篇预告。

         下一篇中我们将会讲述:简单讲述其他的UML建模图,并且结合B2C实例,详细分析B2C系统应该具有的功能模块。及每个模块通过不同的建模图形的表示方

法,及如何在功能分析时使用恰当的建模图。

后语

      希望看完本章的朋友可以从本篇中学到相应的UML建模知识,懂的人可以巩固下UML知识,本篇希望能够抛砖引玉,希望大家能够多提出宝贵意见。由于是本人

平时工作中的理解与总结,不足之处再所难免,还请大家批评指出!如果您有什么意见或建议,请多多提出!大家的支持就是我的最大动力!

作者:hegezhou 发表于2011/9/10 12:25:27 原文链接
阅读:2019 评论:0 查看评论

系统架构师-基础到企业应用架构-系统建模[下篇]

$
0
0

一、上章回顾

       上一篇:系统架构师-基础到企业应用架构-系统建模[中篇](下) 中我们主要讲解了部署图、活动图,我们在这里也是参考上篇的形式,这里不再详细介绍。上篇主

要讲解了下面2类建模图:

        image

二、摘要

       本文将讲解其他的几个类型的建模图当然只是简单的讲解,并且将结合B2C电子商城系统进行分析通过使用我们已经讲解的建模图为例。分析系统可划分的子功能

模块,每个功能模块内部的运行步骤等等。

       image image

       上面的2个不同类型的进行划分的建模图,本章将对上述6个建模图进行分别举例讲解。

三、本章内容

       1、上章回顾。

       2、摘要。

       3、本章内容。

       4、结构图。

       5、行为图。

       6、本章总结。

       7、系列进度。

       8、下篇预告。

四、结构图

      1、对象图

        首先、我们闲来讲解对象图。对象图用来描述系统的各个对象在某一时刻的状态。对象和类图一样他们是静态结构图。他们是从实际的或者原型化的场景去表达

的。对象图显示了某一时刻对象与对象的关系。一个对象图可以看作类图的特殊用例,类图中的关系同样适用在对象图中。可以这样理解,对象图就是类图的实例。对

象图是有生命周期的,因此对象图只在某个时间段内存在。

        对象图中的元素在类图中都可以找到,只是把类图中的类元素换成对象即可。而类图中类元素之间的关系,在对象图中同样适用。这里不在复述。如果对类图不

是特别的熟悉,请看这篇文章中的讲解:系统架构师-基础到企业应用架构-系统建模[上篇]

        下面讲解对象图的举例:

        image 这里的对象是指某个类的实例。

        image 这样的格式表示了某个类的实例的格式,冒号“:”后面跟着类名,也就是这里的“父类”。另外还必须加上下划

线。

        对象首先是一个确定,所以一般情况下,对象属性一般把值直接列出来。如下形式:

        image

        对象图中的所有的对象名可以为空,但是为了更好的标识对象图中的对象,不建议这么做,并且如果未指定对象名那么必须指定该对象所属的类格式如下:

        image 没有对象名的对象实例。

        下面以B2C中的订单系统中的新订单的状态为例,讲述下各对象的状态。

        image 这里的关系表示的是组合关系

        上图中的订单信息的状态:订单(新订单)-物流信息(未发货)-支付信息(未支付)-产品状态(产品信息)。

      2、包图

        包图就是由包与包之间的关系组成的。

        包图也是一种静态结构,包可以拥有的元素:

        image

        我想上面的元素大家都是有所了解的,我这里就不一样举例说明了,下面通过一个例子来显示如何使用包图。

        包的访问限制:与我们平时了解的3个访问权限设置关键字用法相同。

        image

       包与包之间的关系:

       a、引入与访问依赖:首先这个关系与平时我们说的类的继承关系是不同的.包括包的访问域不能继承。

       image 用于在一个包中引入另一个包输出的元素,因此A依赖B,包A引入包B中的B方法。B这里的访问权限是公共的。A中的方法是保护的。

       b、泛化关系:       

       image 泛化关系描述了一种继承关系。即基类与特殊类之间的关

系,途中描述的意思是只要是包A出现的位置都可以用包B替换。

      3、组合结构图

        组合结构图:以结构化的方式给出类型的内部结构。组合结构图是一种静态结构,它显示了一个类型内部的成员及成员之间的关系。组合结构图可以这样理解,

就是描述类的内部结构及成员之间的调用关系的建模图。组合结构图用于扑捉类的内部细节,描述了对象如何在某个类中协同工作。

       组合图中其实就是描述类的内部的结果,基本上的元素有:类、对象,其他等,具体的关系请参考类图中的关系。

       组合图实例:

       image 上图显示了产品与产品品牌与产品分类的组合关系。产品品牌与产品分类是关联关系(关联关系可

以是1:N),通过一条直线来链接。如果有不清楚的地方请看类图的相关介绍:系统架构师-基础到企业应用架构-系统建模[上篇]

五、行为图

      1、通信图

        在UML建模中除了顺序图(序列图)可以表示对象之间的交互外,通信图也可以完成通用的描述。一般情况下,二类图可以进行互转。

        首先、我们先来分析下2类图的不同。

       image

        我们还是先来看下通信图中的元素:

       image

        上图中的主要组成元素是对象。对象之间的关系,通过链接来完成。然后通过一个带有实体三角形的线段指向要发送消息的对象。

        下面来解释下对象之间的关系。

        image

        image 还有就是消息给自己发送的特殊消息。

        返回消息的类型:

        image

        下面我们来简单举例如何说明:

        image 上图中简单描述了发送邮件的过程。我们可以看出通信图相比顺序图更注重对象之间的

链接关系。我们通过通信图能够知道一个系统中的某个流程中各对象直接的链接关系及传递的完整信息传递过程。

        首先、我们闲来讲解对象图。对象图用来描述系统的各个对象在某一时刻的状态。

      2、时间图

        时间图:主要用来描述一段时间内对象的行为(状态的变化,执行的动作等等)。主要用来模拟即时系统模型。与对象图类似,不同的是时间图描述的一个时间段,而

对象图描述的一个时间点。对象图关心对象的状态,而时间图关心的是对象的行为。

        image 这里设置了定时器之后,对象的状态将会从A,在定时器时间到达后自动的跳转

到状态B。这里表示的是定时器来控制状态的迁移。

       时间图中支持异步信息,时间图采用垂直图的方式来描述系统的执行顺序。

       时序图等于是在序列图(顺序图上)加上时间的限制就构成了时间图。

       image 从垂直的角度来看,就是说A调用B先于B返回值给A,从时间图上就可以清晰的看出来对象的行

为。 一般情况下我们可以通过序列图来完成相应的描述。除非需要定义一个时间段的对象时才会用到时间图。包括某个对象到某个对象的调用都可以通过时间限制来进

行行为的控制等。

六、本章总结。

        本章中主要简单的讲述了几类UML2.0语言相比UML1.0新增的几类建模图,虽然我们平时可能用的比较少,但是某些特定的领域范围,通过这些图可能更能清晰的

表达抽象模型。

         1、对象图:描述系统在某一时刻的状态。

         2、包图:描述系统中的某个模块的内部组成结构,是由元素及元素之间的关系构成的。

         3、组合结构图:以结构化的方式描述了系统中的某个类型的内部结构。

         4、通信图:描述了对象之间的交互与顺序图不同的是,交互图更关注对象之间的链接及通信。

         5、时间图:描述了在一个时间段内的对象的行为。

        当然具体的建模图如何使用及在实际的项目中如何去应用分析,这才是最主要的目的,当然这就需要理论结合实际来达到学以致用的效果。下一篇,我讲专门的讲

解针对B2C电子商城为例,详细的剖析UML建模的具体应用。

七、系列进度。

     前篇

      1、系统架构师-基础到企业应用架构系列之--开卷有益

      2、系统架构师-基础到企业应用架构-系统建模[上篇]

      3、系统架构师-基础到企业应用架构-系统建模[中篇](上)

      4、系统架构师-基础到企业应用架构-系统建模[中篇](下)

      5、系统架构师-基础到企业应用架构-系统建模[下篇]

      6、系统架构师-基础到企业应用架构-系统设计规范与原则[上篇]

      7、系统架构师-基础到企业应用架构-系统设计规范与原则[下篇]

      8、系统架构师-基础到企业应用架构-设计模式[上篇]

      9、系统架构师-基础到企业应用架构-设计模式[中篇]

      10、系统架构师-基础到企业应用架构-设计模式[下篇]

     中篇

      11、系统架构师-基础到企业应用架构-企业应用架构

      12、系统架构师-基础到企业应用架构-分层[上篇]

      13、系统架构师-基础到企业应用架构-分层[中篇]

      14、系统架构师-基础到企业应用架构-分层[下篇]

      15、系统架构师-基础到企业应用架构-表现层

      16、系统架构师-基础到企业应用架构-服务层

      17、系统架构师-基础到企业应用架构-业务逻辑层

      18、系统架构师-基础到企业应用架构-数据访问层

      19、系统架构师-基础到企业应用架构-组件服务

      20、系统架构师-基础到企业应用架构-安全机制

     后篇

      21、单机应用、客户端/服务器、多服务、企业数据总线全解析

      22、系统架构师-基础到企业应用架构-单机应用(实例及demo)

      23、系统架构师-基础到企业应用架构-客户端/服务器(实例及demo)

      24、系统架构师-基础到企业应用架构-多服务(实例及demo)

      25、系统架构师-基础到企业应用架构-企业数据总线(实例及demo)

      26、系统架构师-基础到企业应用架构-性能优化(架构瓶颈)

      27、系统架构师-基础到企业应用架构-完整的架构方案实例[上篇]

      28、系统架构师-基础到企业应用架构-完整的架构方案实例[中篇]

      29、系统架构师-基础到企业应用架构-完整的架构方案实例[下篇]

      30、系统架构师-基础到企业应用架构-总结及后续

八、下篇预告。

       下一篇将会已我比较熟悉的B2C电子商城来深入的剖析,如何使用UML建模工具去分析一个系统及如何分析一个模块的流程,包括部署等等。通过我们已经讲过的

UML建模图形来结合实例来讲解。如果大家有好的意见和建议可以及时反馈,谢谢您的宝贵意见。

后语

       希望看完本章的朋友可以从本篇中学到相应的UML建模知识,懂的人可以巩固下UML知识,本篇希望能够抛砖引玉,希望大家能够多提出宝贵意见。由于是本人

平时工作中的理解与总结,不足之处再所难免,还请大家批评指出!如果您有什么意见或建议,请多多提出!大家的支持就是我的最大动力!

作者:hegezhou 发表于2011/9/10 12:26:08 原文链接
阅读:2435 评论:0 查看评论

老子的软件之道 - 道篇 24 设计师守则

$
0
0
摘要:老子哲学 道德经 软件哲学、软件之道               参阅:  消灭人狼  软件的十大命题 编程规则  

      圣人曰:企者不立;跨者不行。自见者不明;自是者不彰。自伐者无功;自矜者不长。其在道也曰∶馀食赘形。物或恶之,故有道者不处。

      “踮起脚跟,想高人一头,但你很难站稳、很难持久。将两腿使劲跨开想超有别人,但反而无法行走。”圣人用这样简单的例子告诉我们一个深刻的道理:实事求是,按自然规律办事!一切形式的主观的、激进的行为都是背道而驰的。只有遵循客观规律,脚踏实地,循序渐进,具有诚心和恒心,才能达到目的。

      还记得3.4大师风范中圣人关于“自见、自是、自伐、自矜”的论述吗?本节作为守则,要求设计师摒弃如下不良行为:

      固执己见,容易导致不能接受正确观点,因此对事物的认识会出现偏门,不利于综合多方观点搞清事物本质;

      自以为是,认为自己的观点和做法总是正确,从不听取他人的意见和建议;过分主观、不虚心。这必然会导致一叶障目,事物的本源就难以彰显,更得不到多数人的配合和支持。

      自我夸耀,是名利之心的膨胀和表现,这样会受到大家的鄙视,因此无法获得成功;

      自高自大,是骄傲自满的表现,这样的人就无法进步了,也就不可能取得新的成就。

      以上行为都是多余的、无用的,更是有害的和令人厌恶的,遵循软件之道的设计师是绝不会有类似行为的。

 

      作为软件设计师你必须遵守上面的总则,此外你还要遵守如下具体规则,这样你才能逐步成长为卓越的设计师。

       1)追本求源;深入研究涉及的业务领域,把握它稳定、本质的逻辑和流程,这是良好设计的基础;不要被表面现象蒙蔽,面向功能的设计必然是平庸的设计!不能完成具体功能要求的设计,则是失败的设计!就如国画大师白石老人所云:“太似则媚俗,不似则欺世”,大道相通啊!

       2)抽象;必须对该领域内的概念、数据、功能等进行反复认真的综合分析,并抽象出应用领域稳定的业务模型,对业务领域的本质抽象是卓越设计的关键。

       3)追求完美、精益求精;少于三个方案的设计就不是设计!低于五次修改的设计一定不是一个优秀的设计

       4)不断学习实践,勇于批判和质疑;学习、实践、体会、思考、批判、总结、再实践,“批评性实践”是设计师的可贵精神。

       5)热爱设计,追求卓越;建立高远的信念;设计给你带来快乐,设计使你的生命更丰富,设计可以改变世界。

作者:xabcdjon 发表于2011/9/10 19:07:13 原文链接
阅读:23183 评论:4 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>