wsl配置代理

wsl1适用版:

1
2
3
4
5
6
7
8
9
10
11
12
13
hostip="127.0.0.1"
socks_hostport=10808
http_hostport=10809
export all_proxy="socks5://${hostip}:${socks_hostport}"
export http_proxy="http://${hostip}:${http_hostport}"
export https_proxy="http://${hostip}:${http_hostport}"
export ftp_proxy=$http_proxy
export rsync_proxy=$http_proxy
export ALL_PROXY=$all_proxy
export HTTP_PROXY=$http_proxy
export HTTPS_PROXY=$https_proxy
export FTP_PROXY=$ftp_proxy
export RSYNC_PROXY=$rsync_proxy

特点:
因为wsl1在逻辑上与宿主机属于相同的主机,ip直接使用127.0.0.1

wsl2适用版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Set network proxy
hostip=$(ip route | grep default | awk '{print $3}')
socks_hostport=10810
http_hostport=10811
export all_proxy="socks5://${hostip}:${socks_hostport}"
export http_proxy="http://${hostip}:${http_hostport}"
export https_proxy="http://${hostip}:${http_hostport}"
export ftp_proxy=$http_proxy
export rsync_proxy=$http_proxy
export ALL_PROXY=$all_proxy
export HTTP_PROXY=$http_proxy
export HTTPS_PROXY=$https_proxy
export FTP_PROXY=$ftp_proxy
export RSYNC_PROXY=$rsync_proxy
export no_proxy="localhost,127.0.0.1"

特点:
因为wsl2在逻辑上与宿主机属于不同的主机,
使用ip route | grep default | awk '{print $3}'获取宿主机ip
另外需要设置no_proxy,否则wsl2运行的程序试图访问wsl2本机时也会被错误转发到宿主机代理然后访问成宿主机,导致一些程序出错。

WSL2 Error code: Wsl/Service/0x8007273d解决

首先说结论,这个问题(在我所遇到的情况中)大多数是由于加速器软件或者全局代理类软件修改了系统的Winsock来全局加载代理导致的,而WSL2与其不兼容,所以产生 0x8007273d 错误。

你可以通过简单地命令行中运行netsh winsock reset(管理员权限,本文后续也默认在管理员权限cmd中运行)来重置Winsock,然后 WSL2 就可以正常工作了。
但也这会使 Proxifier等(使用 winsock 的软件)不再能工作。

如果你希望能使用此类软件也能使用WSL2,请继续向下看:

我首先通过搜索找到了这样一篇文章:https://wangyj.medium.com/the-solution-to-wsl-error-the-attempted-operation-is-not-supported-for-the-type-of-object-aa559854d1e3

我发现它的思路是正确的,但对当前最新版本的WSL2(从Micosoft store安装或更新的)而言不适用,需要做一些小调整。
下面分别详细介绍早期版本和当前最新版的详细修复方法。

早期版本的步骤(如果你是新安装的Win系统或从 “启用或关闭Windows功能” 安装的wsl):

  1. 下载 Nolsp.exe:http://www.proxifier.com/tmp/Test20200228/NoLsp.exe
  2. 通过 NoLSP.exe "C:\Windows\System32\wsl.exe"配置 nolsp,它会添加适当的注册表项。
  3. 关闭所有 wsl 终端并重新打开 wsl
    现在,wsl2 可以正常工作了。

(替代方法)如果不想使用 Nolsp.exe 二进制文件,可以使用 regedit.exe 在注册表中手动添加以下项目。

1
2
3
4
5
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\AppId_Catalog\0408F7A3]
"AppFullPath"="C:\Windows\System32\wsl.exe"
"PermittedLspCategories"=dword:80000000

(或保存为.reg文件运行导入)

如果你是从 Micosoft store 安装的WSL,或者你曾经运行过 wsl --update (也是 Store 版 WSL):

这两种情况使用的是 Micosoft Store 中的 wsl2,它们真正执行主要功能的程序路径不同,因此在修复步骤中需要指定 WinApps 应用程序的文件路径(”C:\Program Files\WindowsApps\WSl安装文件夹名\wsl.exe”),而不是系统目录中”C:\Windows\System32\wsl.exe”,另外,最新版本还涉及wslservice.exe的文件(它作为后台服务运行,也需要重启)

首先,你可以通过在 cmd(admin) 中执行以下指令来查看WSl安装文件夹名:

1
2
cd "C:\Program Files\WindowsApps\"
dir MicrosoftCorporationII.WindowsSubsystemForLinux_*

你会看到WSl安装文件夹名,格式类似 MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe,复制它并且在后续步骤中的路径中替换WSl安装文件夹名,完整的路径类似C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe\wsl.exe
(不同的 wsl2 版本有不同的安装文件夹名。

步骤:

  1. 下载 Nolsp.exe: http://www.proxifier.com/tmp/Test20200228/NoLsp.exe
  2. 运行 NoLSP.exe "C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe\wsl.exe", 它会添加一个正确的注册表。
  3. 运行 NoLSP.exe "C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe\wslservice.exe" ,它将添加一个正确的注册表。
    (注意:如果你使用的是不同的版本,记得在以上两步使用你的WSl安装文件夹名来替换掉1.2.5.0_x64__8wekyb3d8bbwe
  4. 运行 wsl --shutdown && net stop WslService && net start WslService,关闭所有 wsl 终端并重新打开 wsl
    现在,wsl2 可以正常工作了。

另外,请注意,每次 wsl 更新后,WindowsApps 的路径都会发生变化(因此将来更新后需要重新配置 nolsp)。

查看gcc编译器的默认include目录

有时候我们在配置一些代码编辑器的intellisense功能时,需要添加编译时的系统头文件列表,而这些不是太容易寻找(可能有很多个目录组成),这时候我们可以使用以下方法:


运行gcc -xc++ -E -v -
该命令通过指定C++语言选项-xc++来启动GCC编译器,并使用-E选项告诉它仅进行预处理,-v选项启用详细输出。最后的-表示从标准输入中读取代码。

运行上述命令后,GCC将输出许多详细信息,可以在输出中找到类似以下内容的行:

1
2
3
4
5
#include "..." search starts here:
#include <...> search starts here:
/path/to/include/dir1
/path/to/include/dir2
...

这些目录路径就是gcc编译器的默认include目录。

如果不需要c++的头文件而只需要c语言的头文件,也可以将选项-xc++换成-xc。

负二进制转换与负M进制转换

LeetCode原题“1017. 负二进制转换”:https://leetcode.cn/problems/convert-to-base-2/

给你一个整数 n ,以二进制字符串的形式返回该整数的 负二进制(base -2)表示。

注意,除非字符串就是 “0”,否则返回的字符串中不能含有前导零。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
示例 1:

输入:n = 2
输出:"110"
解释:(-2)2 + (-2)1 = 2
示例 2:

输入:n = 3
输出:"111"
解释:(-2)2 + (-2)1 + (-2)0 = 3
示例 3:

输入:n = 4
输出:"100"
解释:(-2)2 = 4

本题给出了负二进制的转换

给你一个整数 n ,以二进制字符串的形式返回该整数的 负二进制(base -2)表示。

注意,除非字符串就是 “0”,否则返回的字符串中不能含有前导零。

即,a_0*(-2)^0 + a_1*(-2)^1 +... = n,其中ai在负二进制下为0或1,求a_i序列

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
string baseNeg2(int n) {
if (n == 0 || n == 1) {
return to_string(n);
}
string res;
while (n != 0) {
int remainder = n & 1;
res.push_back('0' + remainder);
n -= remainder;
n /= -2;
}
reverse(res.begin(), res.end());
return res;
}
};

// 作者:LeetCode-Solution

那么现在提问,进行扩展:

  1. 如果需要转换为任意负M进制呢?
  2. 如何同时支持正进制和负进制?

即,a_0*M^0 + a_1*M^1 +... = n,其中ai在负M进制下为0到-N-1,在正M进制下为0到N-1,求a_i序列

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
const int M = -2;
string baseNeg2(int n) {
if(n==0) return "0"s;
string ans;
while(n) {
int remainder = n % toM;
if(remainder<0) {
// n=M*quotient+remainder
// 当n为正数而M是负数时,n%M的结果remainder在大多数编程语言(c/c++/java)也是负数,remainder调回正数只需要-M(加上M的绝对值)
// 同时为了保持n不变,商需要+1(M*quotient为更大的负)来抵消掉多出来的部分
remainder-=M;
n = n/M+1;
} else {
// 当余数恰好为0 或者 n和M同号(此时remainder为正),不需要任何处理
n /= M;
}
ans.push_back('0' + remainder);
}
reverse(ans.begin(),ans.end());
return ans;
}
};

参考资料:

跳表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
const double PFACTOR = 0.25;
const int MAX_LEVEL = 32;
class Skiplist {
struct Node {
int val;
vector<Node*> forward;
Node(int _val, int level): val(_val), forward(level, nullptr) {
}
};
random_device rd;
mt19937 gen{rd()};
uniform_real_distribution<double> dis{0, 1};
public:
Node* head;
int level;
Skiplist() {
head = new Node(-1, MAX_LEVEL);
level = 0;
}
bool search(int target) {
Node *cur=this->head;
for(int i=level-1; i>=0; i--) {
while(cur->forward[i] && cur->forward[i]->val < target) {
cur = cur->forward[i];
}
}
cur = cur->forward[0];
return cur && cur->val == target;
}
void add(int num) {
int lv=randomLv();
level=max(level, lv);
array<Node*, MAX_LEVEL> shouldupdate;
Node *cur=this->head;
for(int i=level-1; i>=0; i--) { // 找到各层要更新的结点
while(cur->forward[i] && cur->forward[i]->val < num) {
cur = cur->forward[i];
}
shouldupdate[i] = cur; // 虽然全部记录了但是并不是全部都用了
}
Node *newNode=new Node(num, lv);
for(int i=0; i<lv; i++) { // 只有前Lv层(结点所在那些层级)的前序节点才会被更新
newNode->forward[i] = shouldupdate[i]->forward[i];
shouldupdate[i]->forward[i] = newNode;
}
return;
}

bool erase(int num) {
array<Node*, MAX_LEVEL> shouldupdate;
Node *cur=this->head;
for(int i=level-1; i>=0; i--) {
while(cur->forward[i] && cur->forward[i]->val < num) {
cur = cur->forward[i];
}
shouldupdate[i] = cur;
}
cur = cur->forward[0]; // 最底层再推进一次应该就是要删除的元素
if (!(cur && cur->val == num)) return false; // 说明当前skiplist不存在该元素
// 从最底0层开始更新前序节点,直到没有这个元素的层数上
for(int i=0; i<level && shouldupdate[i]->forward[i]==cur; i++) {
shouldupdate[i]->forward[i] = cur->forward[i];
}
delete cur; // 删除节点本身
while(level>1 && !head->forward[level-1]) { // 如果删除节点使得顶上几层变为了空,我们需要删除顶上这些层
level--;
}
return true;
}
private:
int randomLv() {
int lv=1;
while(dis(gen)<PFACTOR && lv<MAX_LEVEL) lv++;
return lv;
}
};

/**
* Your Skiplist object will be instantiated and called as such:
* Skiplist* obj = new Skiplist();
* bool param_1 = obj->search(target);
* obj->add(num);
* bool param_3 = obj->erase(num);
*/

Fisher-Yates 洗牌算法

可以原地实现打乱数组。

1
2
3
4
5
6
7
vector<int> nums; int n=nums.size();
for(int i=0; i<n; i++) {
uniform_int_distribution<> dis(i, n-1); // range is [i, n-1]
int j=dis(gen);
swap(nums[i], nums[j]);
}
return nums;

该程序循环n次,每次从[i, n-1]选取一个数字,然后把它交换到第i位。

概率分析:
原数组中的任意一个数字,在第i次被选出从而放到第i个位置的概率是

1
2
3
4
P(0)=1/n
P(1)=(n-1/n)*(1/n-1)=1/n
...
P(i)= (n-1/n)*(n-2/n-1)*...*(1/n-i)=1/n

所以每个数字出现在每个位置的概率等同。

全排列中获取下一个排列的实现(c++ STL next_permutation)

维基百科中的步骤:

  1. Find the largest index k such that a[k] < a[k + 1]. If no such index exists, the permutation is the last permutation.
  2. Find the largest index l greater than k such that a[k] < a[l].
  3. Swap the value of a[k] with that of a[l].
  4. Reverse the sequence from a[k + 1] up to and including the final element a[n].

至于为什么这么做就可以得到下一个排列,可以看代码及注释。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public:
void nextPermutation(vector<int>& nums) {
if(nums.size()==1) return;
// find the largest index i which statisfies nums[i]<nums[i+1]
// 从后往前找到交换点i,交换点i以后的数全部都是降序的
// 因为i后面的数[i+1, n)已经无法变得更大,所以我们不得不让i位置变大一点来得到下一个序列
int i=nums.size()-2;
while(i>=0 && nums[i]>=nums[i+1]) {
i--;
}
if(i<0) {
// 如果找不到这么一个位置i,表示整个序列都是降序的,即已经是最后一个排列。
// 重新得到第一个排列的方法是整个数组逆序。
reverse(nums.begin(), nums.end());
return;
}
// 可想而知,为了是变大程度尽量小,i前面的数不应该改动,我们从i后面的数里面挑出一个比i稍大的
// 因为后面是降序,所以可以直接按位置查找
// find the largest index j which statisfies nums[j]>nums[j]
int j=nums.size()-1;
while(nums[j] <= nums[i]) {
j--;
}
// 我们将这个数nums[j]交换到nums[i]的位置
swap(nums[i], nums[j]);
// 因为我们找到的nums[j]是比nums[i]刚刚稍小的数,所以交换完成后,后面的区间[i+1,n]仍然是降序的
// 此时还没有结束。因为后面的区间[i+1,n]是降序的,我们知道降序是最大的排列(最后一个排列)而不是最小的排序
// 我们将这个降序区间调整回为升序区间,此时才是下一个排列
reverse(nums.begin()+i+1, nums.end());
}
};

相关练习题:https://leetcode.cn/problems/next-permutation

参考资料:
https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-leetcode-solution/
https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-powcai/
https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order

wsl2配置代理

wsl1中主机和wsl共享网络,但在wsl2中是单独的网络。
可以借助如下命令配置代理(可写在~/.profile里)

1
2
3
4
5
6
7
8
9
10
11
host_ip=$(cat /etc/resolv.conf |grep "nameserver" |cut -f 2 -d " ")

# system proxy
export ALL_PROXY="http://$host_ip:10809"

# go proxy
GOPROXY="https://goproxy.cn,direct"

# git proxy
git config --global http.proxy socks5://$host_ip:10808
git config --global https.proxy socks5://$host_ip:10808

记得打开代理软件的接受来自局域网的连接

请我喝杯咖啡吧~

支付宝
微信