雪花演算法
1. 如何保證資料庫集群中id的唯一性,假設每秒鍾並發20萬次
用雪花演算法的工具類,1秒內可以生成26萬不重復的值,資料庫的主鍵不要自增,手動設置
java">packageentity;
importjava.lang.management.ManagementFactory;
importjava.net.InetAddress;
importjava.net.NetworkInterface;
/**
*<p>名稱:IdWorker.java</p>
*<p>描述:分布式自增長ID</p>
*<pre>
*Twitter的SnowflakeJAVA實現方案
*</pre>
*核心代碼為其IdWorker這個類實現,其原理結構如下,我分別用一個0表示一位,用—分割開部分的作用:
*1||0------00000---00000---000000000000
*在上面的字元串中,第一位為未使用(實際上也可作為long的符號位),接下來的41位為毫秒級時間,
*然後5位datacenter標識位,5位機器ID(並不算標識符,實際是為線程標識),
*然後12位該毫秒內的當前毫秒內的計數,加起來剛好64位,為一個Long型。
*這樣的好處是,整體上按照時間自增排序,並且整個分布式系統內不會產生ID碰撞(由datacenter和機器ID作區分),
*並且效率較高,經測試,snowflake每秒能夠產生26萬ID左右,完全滿足需要。
*<p>
*64位ID(42(毫秒)+5(機器ID)+5(業務編碼)+12(重復累加))
*
*@authorPolim
*/
publicclassIdWorker{
//時間起始標記點,作為基準,一般取系統的最近時間(一旦確定不能變動)
privatefinalstaticlongtwepoch=1288834974657L;
//機器標識位數
=5L;
//數據中心標識位數
=5L;
//機器ID最大值
=-1L^(-1L<<workerIdBits);
//數據中心ID最大值
=-1L^(-1L<<datacenterIdBits);
//毫秒內自增位
=12L;
//機器ID偏左移12位
=sequenceBits;
//數據中心ID左移17位
=sequenceBits+workerIdBits;
//時間毫秒左移22位
=sequenceBits+workerIdBits+datacenterIdBits;
=-1L^(-1L<<sequenceBits);
/*上次生產id時間戳*/
=-1L;
//0,並發控制
privatelongsequence=0L;
privatefinallongworkerId;
//數據標識id部分
privatefinallongdatacenterId;
publicIdWorker(){
this.datacenterId=getDatacenterId(maxDatacenterId);
this.workerId=getMaxWorkerId(datacenterId,maxWorkerId);
}
/**
*@paramworkerId
*工作機器ID
*@paramdatacenterId
*序列號
*/
publicIdWorker(longworkerId,longdatacenterId){
if(workerId>maxWorkerId||workerId<0){
(String.format("workerIdcan'tbegreaterthan%dorlessthan0",maxWorkerId));
}
if(datacenterId>maxDatacenterId||datacenterId<0){
(String.format("datacenterIdcan'tbegreaterthan%dorlessthan0",maxDatacenterId));
}
this.workerId=workerId;
this.datacenterId=datacenterId;
}
/**
*獲取下一個ID
*
*@return
*/
publicsynchronizedlongnextId(){
longtimestamp=timeGen();
if(timestamp<lastTimestamp){
thrownewRuntimeException(String.format("Clockmovedbackwards.Refusingtogenerateidfor%dmilliseconds",lastTimestamp-timestamp));
}
if(lastTimestamp==timestamp){
//當前毫秒內,則+1
sequence=(sequence+1)&sequenceMask;
if(sequence==0){
//當前毫秒內計數滿了,則等待下一秒
timestamp=tilNextMillis(lastTimestamp);
}
}else{
sequence=0L;
}
lastTimestamp=timestamp;
//ID偏移組合生成最終的ID,並返回ID
longnextId=((timestamp-twepoch)<<timestampLeftShift)
|(datacenterId<<datacenterIdShift)
|(workerId<<workerIdShift)|sequence;
returnnextId;
}
privatelongtilNextMillis(finallonglastTimestamp){
longtimestamp=this.timeGen();
while(timestamp<=lastTimestamp){
timestamp=this.timeGen();
}
returntimestamp;
}
privatelongtimeGen(){
returnSystem.currentTimeMillis();
}
/**
*<p>
*獲取maxWorkerId
*</p>
*/
(longdatacenterId,longmaxWorkerId){
StringBuffermpid=newStringBuffer();
mpid.append(datacenterId);
Stringname=ManagementFactory.getRuntimeMXBean().getName();
if(!name.isEmpty()){
/*
*GETjvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
*MAC+PID的hashcode獲取16個低位
*/
return(mpid.toString().hashCode()&0xffff)%(maxWorkerId+1);
}
/**
*<p>
*數據標識id部分
*</p>
*/
(longmaxDatacenterId){
longid=0L;
try{
InetAddressip=InetAddress.getLocalHost();
NetworkInterfacenetwork=NetworkInterface.getByInetAddress(ip);
if(network==null){
id=1L;
}else{
byte[]mac=network.getHardwareAddress();
id=((0x000000FF&(long)mac[mac.length-1])
|(0x0000FF00&(((long)mac[mac.length-2])<<8)))>>6;
id=id%(maxDatacenterId+1);
}
}catch(Exceptione){
System.out.println("getDatacenterId:"+e.getMessage());
}
returnid;
}
publicstaticvoidmain(String[]args){
//推特26萬個不重復的ID
IdWorkeridWorker=newIdWorker(0,0);
for(inti=0;i<2600;i++){
System.out.println(idWorker.nextId());
}
}
}
2. 遞歸寫Koch雪花的演算法有哪些
現在繪制3條Koch曲線就構成了Koch雪花。
kochCurve(x0, y0, angle, length, n);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle -= pi*2/3;
kochCurve(x0, y0, angle, length, n);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle -= pi*2/3;
kochCurve(x0, y0, angle, length, n);
下一次的迭代同樣要用到這樣的方法,所以我們可以把這些代碼放倒kochCurve函數中。於是kochCurve成了一個遞歸函數。
為了控制遞歸的深度,我們需要給kochCurve添加一個參數n。
void kochCurve(double x0, double y0, double angle, double length, int n)
{
if(n == 0) {
double x1 = x0 + length*cos(angle);
double y1 = y0 + length*sin(angle);
glBegin(GL_LINES);
glVertex2d(x0, y0);
glVertex2d(x1, y1);
glEnd();
}
else {
length /= 3;
n --;
kochCurve(x0, y0, angle, length, n);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle += pi/3;
kochCurve(x0, y0, angle, length, n);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle -= pi*2/3;
kochCurve(x0, y0, angle, length, n);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle += pi/3;
kochCurve(x0, y0, angle, length, n);
}
}
n是函數遞歸的層數,也是Koch曲線迭代的次數。
kochCurve(x0, y0, angle, length, 0)畫出的是初始的圖形
kochCurve(x0, y0, angle, length, 1)畫出第一次迭代
下面是n = 2, 3, 4, 5的結果。
這里用線段的起點(x0, y0),方向(和正向x軸之間的角度)及長度來描述一條線段。
繪制一條從(-1.0, 0.0)到(1.0, 0.0)的Koch曲線用下面的方法:
double length = 2.0;
double angle = 0.0;
double x0 = -1.0;
double y0 = 0.0;
kochCurve(x0, y0, angle, length);
一條Koch曲線是由4條比例縮寫為整體1/3的Koch曲線組成。
length = 2.0/3;
kochCurve(x0, y0, angle, length);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle += pi/3;
kochCurve(x0, y0, angle, length);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle -= pi*2/3;
kochCurve(x0, y0, angle, length);
x0 += length*cos(angle);
y0 += length*sin(angle);
angle += pi/3;
kochCurve(x0, y0, angle, length);
這樣就畫出了第一次迭代的結果。
理論上說,Koch曲線是由無數無限短的線段組成。繪圖的時候當然不可能畫出無限短的線段。所以我們根據要求,用若干線段組成。
首先,繪制構造Koch曲線的初始圖形,也就是一條直線。
void kochCurve(double x0, double y0, double angle, double length)
{
double x1 = x0 + length*cos(angle);
double y1 = y0 + length*sin(angle);
glBegin(GL_LINES);
glVertex2d(x0, y0);
glVertex2d(x1, y1);
glEnd();
}
3. 遞歸寫Koch雪花的演算法
這里有一個程序 希望可以幫到你 vb寫的Koch雪花遞歸演算法Const pi = 3.14159 Private Sub Form_Click() ScaleTop = 300 ScaleLeft = -75 ScaleWidth = 400 ScaleHeight = -300 Call fractal(50 + 30, 150, 110 + 30, 254, 1) Call fractal(110 + 30, 254, 170 + 30, 150, 1) Call fractal(170 + 30, 150, 50 + 30, 150, 1) End Sub Sub fractal(ax As Single, ay As Single, bx As Single, by As Single, s As Integer) If (bx - ax) * (bx - ax) + (by - ay) * (by - ay) < s Then Line (ax, ay)-(bx, by) Else Dim cx As Single, cy As Single Dim dx As Single, dy As Single Dim ex As Single, ey As Single Dim l As Single Dim alpha As Single cx = ax + (bx - ax) / 3 cy = ay + (by - ay) / 3 ex = bx - (bx - ax) / 3 ey = by - (by - ay) / 3 Call fractal(ax, ay, cx, cy, s) Call fractal(ex, ey, bx, by, s) l = Sqr((ex - cx) * (ex - cx) + (ey - cy) * (ey - cy)) alpha = Atn((ey - cy) / (ex - cx)) If (alpha >= 0 And (ex - cx) < 0) Or (alpha <= 0 And (ex - cx) < 0) Then alpha = alpha + pi End If dy = cy + Sin(alpha + pi / 3) * l dx = cx + Cos(alpha + pi / 3) * l Call fractal(cx, cy, dx, dy, s) Call fractal(dx, dy, ex, ey, s) End IfEnd Sub 祝你好運俄
4. 科克雪花(又稱科赫雪花)的計算方法
雖然不知道這問題被晾在這兒多久了,但是看到網路知道里所有關於科克雪花的面積計算方法給的公式都有點問題,我覺得我需要滾出來一下。(樓上這些真的可以滾開了)
樓主這個文字最好不要我不能做到……只有公式怎麼說方法……。
我們把原始三角形定為第零個圖形,之後的圖形都是正整數編號。
設:原始三角形邊長為a,周長C1為3a,第n個圖形周長為Cn。因為要增加小三角形,每一次變換期間上一個圖形的每一條邊都會有一個長度乘4/3的變化。所以C0=3a,C1=3a×(4/3),C2=3a×(4/3)^2……Cn=3a×(4/3)^n,C∞→∞。
設:原始三角形面積為S,第n個圖形周長為Sn。第一次的變換和之後的不同,每條邊上只增加了一個面積為S/9的小三角形,面積整體增加了S/3。所以S1=4S/3。大部分網路知道的回答都錯在這里,沒有發現這里規律和之後不同。從第三個圖形開始,也就是第二次變換以及往後的變換都遵從同樣的規律:上一個圖形的每一條邊上都增加4個面積為S×(1/9)^n,而第n個圖形的邊數是3×4^(n-2),整合一下每次增加的面積就會是S×3×(4/9)^(n-1)。所以S3=4S/3+S×3×(4/9)^2,S4=4S/3+S×3×(4/9)^3……我查閱了一點資料,雖然自己沒有嘗試換算過,但是化簡可得Sn=(1/5)×(8-3×(4/9)^n)。S∞→8S/5。
這個S∞就是有些網路知道的回答給出的(2√3/5)×(s)^2(他們設的是s為原始三角形邊長)。所以我看了他們有些過程錯了個大的結果還對的就挺……我想這也情有可原吧,網路也就只給出的面積的極限值(情有可原個頭,查查必應查查谷歌就是)。
我沒有講題的天分,沒聽懂→網頁鏈接(看不懂自己翻譯,謝謝)
5. 圖像視頻雪花噪點的檢測演算法實現,有大神有代碼的嗎
主要還是像素檢測。閾值指定。
1:對於一個像素獲取其周圍的像素,均值加權之後和現有的像素值進行比較,
2:計算百分比差異,也可以直接用色差。閾值指定可以通過define全局變數指定值,修改define達到修改閾值的效果。
這個閾值肯定是要你自己指定,視情況修改就好了。
或者統計出全像素的色差值和,算出平均之後取差異大的。
6. 為啥不能用uuid做Mysql的主鍵
在mysql中設計表的時候,mysql官方推薦不要使用uuid或者不連續不重復的雪花id(long形且唯一,單機遞增),而是推薦連續自增的主鍵id,官方的推薦是auto_increment,那麼為什麼不建議採用uuid,使用uuid究竟有什麼壞處?
一、mysql和程序實例 1.1.要說明這個問題,我們首先來建立三張表分別是user_auto_key,user_uuid,user_random_key,分別表示自動增長的主鍵,uuid作為主鍵,隨機key作為主鍵,其它我們完全保持不變.
根據控制變數法,我們只把每個表的主鍵使用不同的策略生成,而其他的欄位完全一樣,然後測試一下表的插入速度和查詢速度:
註:這里的隨機key其實是指用雪花演算法算出來的前後不連續不重復無規律的id:一串18位長度的long值
id自動生成表:
用戶uuid表
隨機主鍵表:
1.2.光有理論不行,直接上程序,使用spring的jdbcTemplate來實現增查測試: 技術框架:springboot+jdbcTemplate+junit+hutool,程序的原理就是連接自己的測試資料庫,然後在相同的環境下寫入同等數量的數據,來分析一下insert插入的時間來進行綜合其效率,為了做到最真實的效果,所有的數據採用隨機生成,比如名字、郵箱、地址都是隨機生成。
package com.wyq.mysqldemo;
import cn.hutool.core.collection.CollectionUtil;
import com.wyq.mysqldemo.databaseobject.UserKeyAuto;
import com.wyq.mysqldemo.databaseobject.UserKeyRandom;
import com.wyq.mysqldemo.databaseobject.UserKeyUUID;
import com.wyq.mysqldemo.diffkeytest.AutoKeyTableService;
import com.wyq.mysqldemo.diffkeytest.RandomKeyTableService;
import com.wyq.mysqldemo.diffkeytest.UUIDKeyTableService;
import com.wyq.mysqldemo.util.JdbcTemplateService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.StopWatch;
import java.util.List;
@SpringBootTest
class MysqlDemoApplicationTests {
@Autowired
private JdbcTemplateService jdbcTemplateService;
@Autowired
private AutoKeyTableService autoKeyTableService;
@Autowired
private UUIDKeyTableService uuidKeyTableService;
@Autowired
private RandomKeyTableService randomKeyTableService;
@Test
void testDBTime() {
StopWatch stopwatch = new StopWatch("執行sql時間消耗");
/**
* auto_increment key任務
*/
final String insertSql = "INSERT INTO user_key_auto(user_id,user_name,sex,address,city,email,state) VALUES( , , , , , , )";
List<UserKeyAuto> insertData = autoKeyTableService.getInsertData();
stopwatch.start("自動生成key表任務開始");
long start1 = System.currentTimeMillis();
if (CollectionUtil.isNotEmpty(insertData)) {
boolean insertResult = jdbcTemplateService.insert(insertSql, insertData, false);
System.out.println(insertResult);
}
long end1 = System.currentTimeMillis();
System.out.println("auto key消耗的時間:" + (end1 - start1));
stopwatch.stop();
/**
* uudID的key
*/
final String insertSql2 = "INSERT INTO user_uuid(id,user_id,user_name,sex,address,city,email,state) VALUES( , , , , , , , )";
List<UserKeyUUID> insertData2 = uuidKeyTableService.getInsertData();
stopwatch.start("UUID的key表任務開始");
long begin = System.currentTimeMillis();
if (CollectionUtil.isNotEmpty(insertData)) {
boolean insertResult = jdbcTemplateService.insert(insertSql2, insertData2, true);
System.out.println(insertResult);
}
long over = System.currentTimeMillis();
System.out.println("UUID key消耗的時間:" + (over - begin));
stopwatch.stop();
/**
* 隨機的long值key
*/
final String insertSql3 = "INSERT INTO user_random_key(id,user_id,user_name,sex,address,city,email,state) VALUES( , , , , , , , )";
List<UserKeyRandom> insertData3 = randomKeyTableService.getInsertData();
stopwatch.start("隨機的long值key表任務開始");
Long start = System.currentTimeMillis();
if (CollectionUtil.isNotEmpty(insertData)) {
boolean insertResult = jdbcTemplateService.insert(insertSql3, insertData3, true);
System.out.println(insertResult);
}
Long end = System.currentTimeMillis();
System.out.println("隨機key任務消耗時間:" + (end - start));
stopwatch.stop();
String result = stopwatch.prettyPrint();
System.out.println(result);
}
user_key_auto寫入結果:
user_random_key寫入結果:
user_uuid表寫入結果
1.4.效率測試結果在已有數據量為130W的時候:我們再來測試一下插入10w數據,看看會有什麼結果:
可以看出在數據量100W左右的時候,uuid的插入效率墊底,並且在後序增加了130W的數據,uudi的時間又直線下降。
時間佔用量總體可以打出的效率排名為:auto_key>random_key>uuid,uuid的效率最低,在數據量較大的情況下,效率直線下滑。那麼為什麼會出現這樣的現象呢?帶著疑問,我們來探討一下這個問題:
二、使用uuid和自增id的索引結構對比 2.1.使用自增id的內部結構自增的主鍵的值是順序的,所以Innodb把每一條記錄都存儲在一條記錄的後面。當達到頁面的最大填充因子時候(innodb默認的最大填充因子是頁大小的15/16,會留出1/16的空間留作以後的 ? ? 修改):
①下一條記錄就會寫入新的頁中,一旦數據按照這種順序的方式載入,主鍵頁就會近乎於順序的記錄填滿,提升了頁面的最大填充率,不會有頁的浪費
②新插入的行一定會在原有的最大數據行下一行,mysql定位和定址很快,不會為計算新行的位置而做出額外的消耗
③減少了頁分裂和碎片的產生
2.2.使用uuid的索引內部結構因為uuid相對順序的自增id來說是毫無規律可言的,新行的值不一定要比之前的主鍵的值要大,所以innodb無法做到總是把新行插入到索引的最後,而是需要為新行尋找新的合適的位置從而來分配新的空間。
這個過程需要做很多額外的操作,數據的毫無順序會導致數據分布散亂,將會導致以下的問題:
①寫入的目標頁很可能已經刷新到磁碟上並且從緩存上移除,或者還沒有被載入到緩存中,innodb在插入之前不得不先找到並從磁碟讀取目標頁到內存中,這將導致大量的隨機IO
②因為寫入是亂序的,innodb不得不頻繁的做頁分裂操作,以便為新的行分配空間,頁分裂導致移動大量的數據,一次插入最少需要修改三個頁以上
③由於頻繁的頁分裂,頁會變得稀疏並被不規則的填充,最終會導致數據會有碎片
在把隨機值(uuid和雪花id)載入到聚簇索引(innodb默認的索引類型)以後,有時候會需要做一次OPTIMEIZE TABLE來重建表並優化頁的填充,這將又需要一定的時間消耗。
結論:使用innodb應該盡可能的按主鍵的自增順序插入,並且盡可能使用單調的增加的聚簇鍵的值來插入新行
2.3.使用自增id的缺點那麼使用自增的id就完全沒有壞處了嗎?並不是,自增id也會存在以下幾點問題:
①別人一旦爬取你的資料庫,就可以根據資料庫的自增id獲取到你的業務增長信息,很容易分析出你的經營情況
②對於高並發的負載,innodb在按主鍵進行插入的時候會造成明顯的鎖爭用,主鍵的上界會成為爭搶的熱點,因為所有的插入都發生在這里,並發插入會導致間隙鎖競爭
③Auto_Increment鎖機制會造成自增鎖的搶奪,有一定的性能損失
附:Auto_increment的鎖爭搶問題,如果要改善需要調優innodb_autoinc_lock_mode的配置
7. 訂單表數據量越來越大導致查詢緩慢, 如何處理
分庫分表. 由於歷史訂單使用率並不高, 高頻的可能只是近期訂單, 因此, 將訂單表按照時間進行拆分, 根據數據量的大小考慮按月分表或按年分表. 訂單ID最好包含時間(如根據雪花演算法生成), 此時既能根據訂單ID直接獲取到訂單記錄, 也能按照時間進行查詢。
8. 求koch雪花曲線java演算法及詳解
額,首先,你先網路下什麼是koch曲線。。。
先別看代碼,看人家的思路。
編程要多自己動手,老是抄別人的,永遠也學不會的。除非你你認為自己是天才,話說,我已經看到過很多天才如此的黯淡下去了。。。
9. 我拍的圖片是下雪時照的,我的問題是怎樣去掉圖片上面的雪花,用演算法怎麼解決,不用PS
這個顯然是需要慢速快門,使用三腳架,用M檔,快門不大於1/10,可以在1~1設置成快門優先,快門速度設成1秒-1/30秒之間,就可以拍出雪花的軌跡了。
10. 雪花的周長怎樣計算為何說它的周長是無窮大的
首先,計算上述分形幾何知識沒有問題,但這里的事實被忽略了,這里n無限大嗎,畢竟,雪花是一種真正的物質,它也會受到現實因素的限制,當n趨向於無窮大時,這個幾何結構的邊長趨向於無窮大,那麼這個邊真的趨向於無窮大嗎,顯然,這是不可能的。雪花是由水分子組成的。水分子有一定的大小。
所以我們可以得到以下公式,由於分形幾何是由無限分形組成的,因此,變化的n是無限的,因此我們可以得到雪花無限周長的結果。當水凝結時,水分子之間會形成一種特定的結構。這是因為當由水形成的晶體生長時,由於水分子本身的結構的影響,它們會在特定的方向上生長,形成特定的結構,生長到一定程度後,它將形成樹枝並繼續以原始方式生長,因此形成了我們所看到的雪花形狀。
關於雪花的周長怎樣計算為何說它的周長是無窮大的的問題,今天就解釋到這里。