在很多机器学习算法中,经常要用到熵的概念。这里先谈下信息量的概念。更详细可参考csdn链接:
https://blog.csdn.net/xg123321123/article/details/52864830
信息量信息量是用来衡量一个事件的不确定性的,也就是说,一个事件发生的概率越大,不确定性越小,则它携带的信息量也就越小。
假设X是一个离散型随机变量,其取值集合为X,概率分布函数为p(x)=Pr(X=x),x∈X,我们定义事件X=x0的信息量为:
当p(x0)=1时,熵将等于0,也就是说该事件的发生不会导致任何信息量的增加。
举例可以参考上面的链接地址
熵的概念熵是用来衡量一个系统的混乱程度的,代表一个系统中信息量的总和,但不是信息量的简单相加,而是取平均值,或者说期望;信息量总和越大,表明这个系统不确定性就越大。
熵是信息量的期望值,它是一个随机变量的确定性的度量。
熵越大,变量的取值越不确定;反之,熵越小,变量取值就越确定。
其他的比如条件熵,相对熵,交叉熵等,可以参考上面的链接地址:
https://blog.csdn.net/xg123321123/article/details/52864830
这篇主要讲交叉熵:
https://blog.csdn.net/rtygbwwwerr/article/details/50778098
dos比较常用的命令如下,其他的参考文末链接:
1)cd --进入指定目录
格式为" CD [路径]",例如"CD HAPPY"。
注意:只能进入当前盘符中的目录。其中CD\为回到根目录,CD..为回到上一层目录。
CD是切换目录命令,不能用来切换驱动器,如果在C盘根目录下使用CD E:\,在你使用命令E:后,E盘的默认目录就是E:\但是当前目录不变,仍然是C:\,如果E盘有个A目录,而你在C盘输入cd e:\a,那么你仍然在C:\,但是E的默认目录就是A,当你输入E:的时候,就会直接到E:\a
2)·dir--显示指定路径上所有文件或目录的信息
它的格式为"dir [盘符:][路径][文件名] [参数]",比如"DIR E:\FF.M3U"。
3) cls--清除显示器屏幕上的内容,使DOS提示符到屏幕左上角。
[格式] cls
2)md(mkdir)--建立目录
它的格式为"md [盘符][路径]",例如"MD TEMP"。
3)rd(rmdir)--删除目录
格式为"RD [盘符][路径]"。
注意:该命令只能删除空目录,并且不能删除当前目录。
5)copy--拷贝文件
格式为"COPY [源目录或文件] [目的目录或文件]",比如COPY C:\*.COM D:\
注意:使用该命令进行文件拷贝时,目的目录一定要存在。
6) del--删除文件
格式为"DEL [盘符][路径][文件名] [参数]",比如"DEL C:\DATA*.BAK"。它有一个参数:"/P",可以使用户在删除多个文件时对每个文件都显示删除询问
7)ren(rename)--改名
格式为"REN [原名] [现名]",7.0以后版本的DOS都支持对文件名和目录名的修改,而以前的DOS只能修改文件名。
8) type--显示文本文件
格式为"TYPE [文件名]",能对文本文件进行查看。
参考地址:
https://blog.csdn.net/u012198382/article/details/25007979
常见的图形编程库,除了 GDI 外还有 GDI+、OpenGL、DirectX等等,GDI 是其中最基础的一个库。所以 GDI 注定了不会有高级应用,有兴趣的就当刷低级怪吧。
在教程的最开始,需要简单的说明一些前置条件。
开发环境与前言
首先是标明开发环境:
操作系统:win7 (xp应该可以,win8未测试),win10可以
使用工具:visual studio 2010(或更高)
窗口创建
以前代码的前置问题,首先本教程内的 GDI 画图,在最开始部分主要是在窗口内部绘制(为避免混乱窗口外部,也就是整个桌面的绘制会在很后面的地方讨论)。因此,这里需要对于创建窗口一定的了解。为了让大家可以直接复制完代码就可以在一个文件里面运行,博主准备的代码是手动动态创建窗口的代码,所以这里创建窗口的代码有点长,不过大家不要怕,我们要关注的只是中间的一小部分。这里博主先把代码贴上:
#include <windows.h>// 用于注册的窗口类名const char g_szClassName[] = "myWindowClass";/** 第四步,窗口过程*/LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){switch(msg){// 窗口绘制消息case WM_PAINT:/** 我们只需要在这里调用我们的 GDI 绘制函数就可以,其他地方可以先无视*/break;// 窗口关闭消息case WM_CLOSE:DestroyWindow(hwnd);break;// 窗口销毁消息case WM_DESTROY:PostQuitMessage(0); // 发送离开消息给系统break
调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议。这种协议规定了该语言的函数中的参数传送方式、参数是否可变和由谁来处理堆栈等问题。不同的语言定义了不同的调用约定。
在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链接器。这项技术通常被称为名称改编(Name Mangling)或者名称修饰(Name Decoration)。许多C++编译器厂商选择了自己的名称修饰方案。
因此,为了使其它语言编写的模块(如Visual Basic应用程序、Pascal或Fortran的应用程序等)可以调用C/C++编写的DLL的函数,必须使用正确的调用约定来导出函数,并且不要让编译器对要导出的函数进行任何名称修饰。
调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译器用来识别函数名称的名称修饰约定等问题。在Microsoft VC++ 6.0中定义了下面几种调用约定,我们将结合汇编语言来一一分析它们:
1、__cdecl
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。__cdecl可以写成_cdecl。
下面将通过一个具体实例来分析__cdecl约定:
在VC++中新建一个Win32 Console工程,命名为cdecl。其代码如下:
int __cdecl Add(int a, int b); //函数声明void main(){Add(1,2); //函数
#include "stdafx.h"#include <windows.h>#include <stdio.h>#include <iostream>DWORD WINAPI myfun1(LPVOID lpParameter); //声明线程函数DWORD WINAPI myfun2(LPVOID lpParameter);using namespace std;int _tmain(int argc, _TCHAR* argv[]){HANDLE h1,h2;//声明句柄变量h1=CreateThread(NULL,0,myfun1,NULL,0,NULL);//创建线程1cout<<"线程1开始运行!\n"<<endl;h2=CreateThread(NULL,0,myfun2,NULL,0,NULL);//创建线程2cout<<"线程2开始运行!"<<endl;//关闭线程句柄对象CloseHandle(h1);CloseHandle(h2);int n=0;while (1){//cout<<"测试是不是循环重复执行"<<n++<<endl;if (getchar()=='q')//如果用户输入字符q{return 0;//程序正常退出}else{Sleep(100);//程序睡眠}}}//分别实现线程函数,并返回值DWORD WINAPI myfun1(LPVOID lpParameter){cout<<"线程1正在运行"<<endl;return 0;}DWORD WINAPI myfun2(LPVOID lpParameter){cout<<"线程2正在运行"<<endl;return 0;}---------------------作者:小伟锅来源:CSDN原文:https://blog.csdn.n
使用unique函数unique函数是一个去重函数,去除相邻中的重复元素(只留一个),返回的是去重后的尾地址。unique只是去除(相邻)的重复元素,因此,为了去除重复的元素,应该,首先对数组/Vector进行排序,这样保证重复元素在相邻的位置。
其中,最关键的是:去除掉相邻重复元素之后,需要保持数组或vector总长度不变,因此最后几位不变,网上很多人说是 并不是删除并不是把重复的元素删除,而是全部放倒数组的后面。实际上这是不对的.
例如,对于一个内容为{2, 2, 5, 5, 6}的vector,执行unique函数以后,vector大小并没有改变,而且并不是会变成{2, 5, 6, 2, 5},而是变成{2,5,6,5,6},并且函数的返回值为:3。
此时需要删除重复元素,只需要将后面的数据全部删除即可。
排序函数sort和去重函数unique都在algorithm头文件中。
例子:
vector<int> ivec = { 2, 2, 5, 5, 6 };sort(ivec.begin(), ivec.end());ivec.erase(unique(ivec.begin(), ivec.end()), ivec.end());//unique(ivec.begin(), ivec.end()); //实践证明,可以看到,unique函数放到最后的并不是重复的元素,网上的很多都是扯淡for (vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); iter++){cout << *iter << " ";}
使用set集合实现:直接看例子吧,因为set集合是不允许有重复元素的,因此这样做也能去除掉重复的元素:
vector<int> ivec = { 2, 2, 5, 5, 6 };set<int> st(ivec.begin(), ivec.end());ivec.assign(st.begin(), st.end());for (vecto
Variable变量的requires_grad的属性默认为False,若一个节点requires_grad被设置为True,那么所有依赖它的节点的requires_grad都为True。
x=Variable(torch.ones(1))w=Variable(torch.ones(1),requires_grad=True)y=x*wx.requires_grad,w.requires_grad,y.requires_gradOut[23]: (False, True, True)
y依赖于w,w的requires_grad=True,因此y的requires_grad=True(类似or操作),有点类似于传递性
volatile=True是Variable的另一个重要的标识,它能够将所有依赖它的节点全部设为volatile=True,其优先级比requires_grad=True高。因而volatile=True的节点不会求导,即使requires_grad=True,也不会进行反向传播,对于不需要反向传播的情景(inference,测试推断),该参数可以实现一定速度的提升,并节省一半的显存,因为其不需要保存梯度。
前方高能预警:如果你看完了前面volatile,请及时把它从你的脑海中擦除掉,因为
UserWarning: volatile was removed (Variable.volatile is always False)
该属性已经在0.4版本中被移除了,并提示你可以使用with torch.no_grad()代替该功能:
x = torch.tensor([1], requires_grad=True)with torch.no_grad():... y = x * 2y.requires_gradFalse@torch.no_grad()def doubler(x):return x * 2z = doubler(x)z.requires_gradFalse
参考文章: https://
(一)MFC 鼠标双击响应单击事件:
https://blog.csdn.net/qq_26669845/article/details/46472827
(二)鼠标点击事件:
https://bbs.csdn.net/topics/360232427
检查一下CXXXDlg头文件的消息响应函数声明//{{AFX_MSG(CXXXDlg)afx_msg void OnLButtonDown(UINT nFlags, CPoint point);//声明//}}AFX_MSG再检查下源文件中的消息映射BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)//{{AFX_MSG_MAP(CXXXDlg)ON_WM_LBUTTONDOWN() //消息映射//}}AFX_MSG_MAPEND_MESSAGE_MAP()如果都正确的话应该可以响应的
(三)消息映射宏 ON_WM_LBUTTONDOWN(),如何将 消息 与 响应函数 关联起来的呢?
BEGIN_MESSAGE_MAP(CDrawView, CView)//{{AFX_MSG_MAP(CDrawView)ON_WM_LBUTTONDOWN()ON_WM_KEYDOWN()//}}AFX_MSG_MAP// Standard printing commandsON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)END_MESSAGE_MAP()
以上代码, 消息映射宏
ON_WM_LBUTTONDOWN() ON_WM_KEYDOWN()
如何将 消息与 映射函数关联起来的呢?
答案:
ON_WM_LBUTTONDOWN()添加一个条目到CDrawView得消息列表里,它是一个数组,这个条目其中有一项为OnLButton()
一维数组
一维数组的动态分配,初始化和撤销都好说,几乎每一本C++教材都会做出详细的说明。具体如下:
动态分配: int *array=new int [n];
初始化:memset(array,0,n*sizeof(array)); (也可以利用一个for循环对其赋值初始化)
撤销:delete [] array;
二维数组
下面来说二维数组的。
动态分配:
二维数组(n行m列)利用new来进行动态分配实际上相当于对n个m元数组进行动态分配,只不过我们不能一味的按照动态分配一维数组的方法来进行这项操作。MSVC目前还没有这般的人性化,具体应该这样做:
int **array;array=new int *[10];for(int i=0;i<10;i++)array[i]=new int [5];
上面的操作完成了一个10行5列的二维数组array[10][5]的动态分配,可以看到我们先动态分配了一个10单元的数组的指针的指针的首地址给**array,然后再对其每个首地址进行遍历,同时完成一个5单元的数组的动态分分配,并把首地址给*array[i],从而最终完成了二维数组array[10][5]的动态分配。我们可以依此类推得到三维以至多维的数组的动态分配方法。
初始化:
如果把一维数组初始化办法照搬过来就会发现对于动态分配的二维数组并不适用。这就要理解到memset这个函数三个参数的含义。MSDNhttp://blog.csdn.net/shanzhizi对memset的描述如下:
Sets buffers to a specified character.
void *memset( void *dest, int c, size_t count );
可见memset只能作用于一个一维数组void *dest,因此最好的办法就是和二维数组的动态分配结合起来,new一个,memset一个。我的理解是memset只作用于一块连续的内存空间,而动态开辟的空间不一定是连续的,所以具体写法如下:
int **array;array=new int *[10];for(int i=0;i<10;i++){
#define Conn(x,y) x##y#define ToChar(x) #@x#define ToString(x) #x
x##y表示什么?表示x连接y,举例说:
int n = Conn(123,456); //结果就是n=123456;char* str = Conn("asdf", "adf")//结果就是 str = "asdfadf";
怎么样,很神奇吧
再来看#@x,其实就是给x加上单引号,结果返回是一个const char。举例说:
char a = ToChar(1);//结果就是a='1';
做个越界试验char a = ToChar(123);结果是a='3';
但是如果你的参数超过四个字符,编译器就给给你报错了!error C2015: too many characters in constant :P
最后看看#x,估计你也明白了,他是给x加双引号
char* str = ToString(123132);//就成了str="123132"
参考:
https://blog.csdn.net/king110108/article/details/51172460